We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
react的火热程度已经达到了94.5k个start,本系列文章主要用简单的代码来实现一个react,来了解JSX、虚拟DOM、diff算法以及state和setState的设计。
提到react,当然少不了vue,vue的api设计十分简单 上手也非常容易,但黑魔法很多,使用起来有点虚, 而react没有过多的api,它的深度体现在设计思想上,使用react开发则让人比较踏实、能拿捏的住,这也是我喜欢react的原因之一。
写react怎么少的了JSX,JSX是什么,让我来看个例子 现在有下面这段代码:
react
JSX
const el = <h3 className="title">Hello Javascript</h3>
这样的js代码如果不经过处理会报错,jsx是语法糖,它让这段代码合法化,通过babel转化后是这样的:
const el = React.createElement( 'h3', { className: 'title' }, 'Hello Javascript' )
这种例子官网首页也有demo
开始编码之前,先介绍两个东西:parcel和babel-plugin-transform-jsx,等会我们用parcel搭建一个开发工程,babel-plugin-transform-jsx是babel的一个插件,它可以将jsx语法转成React.createElement(...)。
parcel
babel-plugin-transform-jsx
babel
jsx
React.createElement(...)
下面我们开始
parcel这里就不介绍了,一句话概况就是为你生成一个零配置的开发环境。
yarn global add parcel-bundler
npm install -g parcel-bundler
simple-react
yarn init -y
npm init -y
package.json
index.html
src
index.js
如果你先麻烦,可以直接下载源码修改。
以上步骤完可能不完整,最好参考parcel里的内容。以上工作完成后,我们需要安装babel-plugin-transform-jsx:
npm insatll babel-plugin-transform-jsx --save-dev 或者 yarn add babel-plugin-transform-jsx --dev
然后添加.babelrc文件,并在该文件中加入下面这段代码:
.babelrc
{ "presets": ["env"], "plugins": [["transform-jsx", { "function": "React.createElement" }]] }
上面代码的意思是 使用transform-jsx插件,并配置为使用React.createElement方法来解析JSX,当然你也可以不用React.createElement和自定义方法,比如preact使用的h方法。
transform-jsx
React.createElement
preact
h
现在我们在index.js里开始编码。 首先写入代码:
const el = <h3 className="title">Hello Javascript</h3>; console.log(el);
我们在什么都不写的情况下,打印看看el是什么。
el
打印报错:React没有定义。 这是因为在.babelrc文件中,我们使用的这段代码起了作用:
["transform-jsx", { "function": "React.createElement" }]
上面说过,它会通过React.createElement方法来转译JSX,那么我们就给出这个方法: 我们把刚才那段代码改变一下:
const React = { createElement: function(...args) { return args[0]; } }; const el = <h3 className="title">Hello Javascript</h3>; console.log(el);
上面代码添加了一个React对象,并在其中添加一个createElement方法,现在再执行一下看看打印出什么:
React
createElement
由打印结果可以看出,jsx在使用React.createElement方法转译时,createElement方法应该是这样的:
createElement({ elementName, attributes, children });
现在我们改写一下createElement方法,让key的名称简单一点:
const React = { createElement: function({ elementName, attributes, children }) { return { tag: elementName, attrs: attributes, children }; } };
现在可以看到打印结果是:
我们再打印个复杂点的DOM结构:
const el = ( <div style="color:red;"> Hello <span className="title">JavaScript</span> </div> ); console.log(el);
和我们想要的结构一样。 其实上面打印出来的就是虚拟DOM,现在我们要做的就是如何把虚拟DOM转成真正的DOM对象并显示在浏览器上。
虚拟DOM
DOM
要想将虚拟dom转成真实dom并渲染到页面上,就需要调用ReactDOM.render,比如:
ReactDOM.render
ReactDOM.render(<h1>Hello World</h1>, document.getElementById('root'));
这段代码转换后的样子:
ReactDOM.render( React.createElement('h1', null, 'Hello World'), document.getElementById('root') );
这时,react会将<h1>Hello World</h1>挂载到id为root的dom下,从而在页面上显示出来。
<h1>Hello World</h1>
现在我们实现render方法:
render
function render(vnode, container) { const dom = createDom(vnode); //将vnode转成真实DOM container.appendChild(dom); }
上面代码中先调用createDom将虚拟dom转成真实DOM,然后挂载到container下。
createDom
然后挂载到container
我们来实现createDom方法:
function createDom(vnode) { if (vnode === undefined || vnode === null || typeof vnode === 'boolean') { vnode = ''; } if (typeof vnode === 'string' || typeof vnode === 'number') { return document.createTextNode(String(vnode)); } const dom = document.createElement(vnode.tag); //设置属性 if (vnode.attrs) { for (let key in vnode.attrs) { const value = vnode.attrs[key]; setAttribute(dom, key, value); } } //递归render子节点 vnode.children.forEach(child => render(child, dom)); return dom; }
由于属性的种类比较多,我们抽出一个setAttribute方法来设置属性:
function setAttribute(dom, key, value) { //className if (key === 'className') { dom.setAttribute('class', value); //事件 } else if (/on\w+/.test(key)) { key = key.toLowerCase(); dom[key] = value || ''; //style } else if (key === 'style') { if (typeof value === 'string') { dom.style.cssText = value || ''; } else if (typeof value === 'object') { // {width:'',height:20} for (let name in value) { //如果是数字可以忽略px dom.style[name] = typeof value[name] === 'number' ? value[name] + 'px' : value[name]; } } //其他 } else { dom.setAttribute(key, value); } }
现在render方法已经完整的实现了,我们将创建ReactDOM对象,将render方法挂上去:
const ReactDOM = { render: function(vnode, container) { container.innerHTML = ''; render(vnode, container); } };
这里在调用render之前加了一句container.innerHTML = '',就不解释了,相信大家都明白。
container.innerHTML = ''
那么万事具备,我们来测试一下,直接上一个比较复杂的dom结构并加上属性:
const element = ( <div className="Hello" onClick={() => alert(1)} style={{ color: 'red', fontSize: 30 }} > Hello <span style={{ color: 'blue' }}>javascript!</span> </div> ); ReactDOM.render(element, document.getElementById('root'));
打开页面,是我们想要的结果:
再看看控制台的dom:
很完美,这是我们想要的东西
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
react的火热程度已经达到了94.5k个start,本系列文章主要用简单的代码来实现一个react,来了解JSX、虚拟DOM、diff算法以及state和setState的设计。
提到react,当然少不了vue,vue的api设计十分简单 上手也非常容易,但黑魔法很多,使用起来有点虚, 而react没有过多的api,它的深度体现在设计思想上,使用react开发则让人比较踏实、能拿捏的住,这也是我喜欢react的原因之一。
JSX
写
react
怎么少的了JSX
,JSX
是什么,让我来看个例子现在有下面这段代码:
这样的js代码如果不经过处理会报错,jsx是语法糖,它让这段代码合法化,通过babel转化后是这样的:
这种例子官网首页也有demo
准备开始
开始编码之前,先介绍两个东西:
parcel
和babel-plugin-transform-jsx
,等会我们用parcel搭建一个开发工程,babel-plugin-transform-jsx
是babel
的一个插件,它可以将jsx
语法转成React.createElement(...)
。下面我们开始
简单的搭建
parcel这里就不介绍了,一句话概况就是为你生成一个零配置的开发环境。
yarn global add parcel-bundler
或npm install -g parcel-bundler
simple-react
simple-react
中执行yarn init -y
或npm init -y
生成package.json
index.html
src
文件夹 再在src
下创建index.js
然后再index.html
中引入index.js
如果你先麻烦,可以直接下载源码修改。
以上步骤完可能不完整,最好参考parcel里的内容。以上工作完成后,我们需要安装
babel-plugin-transform-jsx
:然后添加
.babelrc
文件,并在该文件中加入下面这段代码:上面代码的意思是 使用
transform-jsx
插件,并配置为使用React.createElement
方法来解析JSX
,当然你也可以不用React.createElement
和自定义方法,比如preact
使用的h
方法。React.createElement()
现在我们在
index.js
里开始编码。首先写入代码:
我们在什么都不写的情况下,打印看看
el
是什么。打印报错:React没有定义。 这是因为在
.babelrc
文件中,我们使用的这段代码起了作用:上面说过,它会通过
React.createElement
方法来转译JSX,那么我们就给出这个方法:我们把刚才那段代码改变一下:
上面代码添加了一个
React
对象,并在其中添加一个createElement
方法,现在再执行一下看看打印出什么:由打印结果可以看出,
jsx
在使用React.createElement
方法转译时,createElement
方法应该是这样的:现在我们改写一下
createElement
方法,让key的名称简单一点:现在可以看到打印结果是:
我们再打印个复杂点的DOM结构:
和我们想要的结构一样。
其实上面打印出来的就是
虚拟DOM
,现在我们要做的就是如何把虚拟DOM
转成真正的DOM
对象并显示在浏览器上。ReactDOM.render()
要想将虚拟dom转成真实dom并渲染到页面上,就需要调用
ReactDOM.render
,比如:这段代码转换后的样子:
这时,react会将
<h1>Hello World</h1>
挂载到id为root的dom下,从而在页面上显示出来。现在我们实现
render
方法:上面代码中先调用
createDom
将虚拟dom转成真实DOM
,然后挂载到container
下。我们来实现
createDom
方法:由于属性的种类比较多,我们抽出一个setAttribute方法来设置属性:
现在
render
方法已经完整的实现了,我们将创建ReactDOM对象,将render方法挂上去:这里在调用
render
之前加了一句container.innerHTML = ''
,就不解释了,相信大家都明白。那么万事具备,我们来测试一下,直接上一个比较复杂的dom结构并加上属性:
打开页面,是我们想要的结果:
再看看控制台的dom:
很完美,这是我们想要的东西
The text was updated successfully, but these errors were encountered: