先看一个初始化jsx的例子
import React from 'react';
import ReactDOM from 'react-dom';
let obj = {color: 'red'};
let ary=[
{id:1,title:'中序遍历的算法原理'},
{id:2,title:'二叉树算法原理及代码实现'}
];
ReactDOM.render(<div id="box"
className="box"
style={obj}>
<h2>javascript和数据结构</h2>
<ul>
{
ary.map((item,index)=>{
return <li key={index}>{item.title}</li>;
})
}
</ul>
</div>, window.root);
复制代码
我们可以看到这样的结果:
jsx语法在这里不做赘述,那么浏览器在jsx语法到真实DOM之间都做了哪些事呢?
- 首先webpack(webpack底层有babel,babel帮我们在es5/6和react做了一个preset)先把JS中所有的JSX元素变为CREATE-ELEMENT模式,我们可以通过在线的babel转译看一下:
React.createElement(
"div",
{ id: "box",
className: "box",
style: styleObj },
React.createElement(
"h2",
{ className: "title" },
"KK\u662F\u4E2A\u6F02\u4EAE\u59D1\u5A18"
),
React.createElement(
"ul",
{ className: "newsItem" },
React.createElement(
"li",
{ key: "1" },
"\u54C8\u54C8\u54C8"
),
React.createElement(
"li",
{ key: "2" },
"\u5475\u5475\u5475"
),
React.createElement("li", { key: "3" })
)
);
复制代码
经过Babel转义的语法在浏览器中会解析出什么呢?我们定义一个变量等于以上代码,看一下结果,是一个对象:
ReactDom.render便是将此对象渲染为真实的DOM,在页面中展示出来,这个过程就是jsx的渲染原理。
所以我们要做的就是模拟createElement和render,代码如下:
class React {
static createElement(type, options, ...arg) {
//=>处理初始值
//=>ARG存储的是剩下的参数集合:即使没有其余的参数了,ARG也是一个空数组
//=>OPTIONS肯定是一个对象(不管传或者没传)
options = options || {};
//=>HANDL
let obj = {
type,//->type:type
key: null,
ref: null,
props: {}
};
//=>KEY && REF
['key', 'ref'].forEach(item => {
if (item in options) {
obj[item] = options[item];
delete options[item];
}
});
//=>PROPS
obj.props = {...options};
let len = arg.length;
switch (len) {
case 0:
//=>没有子节点不需要设置CHILDREN
break;
case 1:
//=>只有一个子节点,CHILDERN不是一个数组
obj.props.children = arg[0];
break;
default:
//=>拥有多个子节点(把深度克隆后的数组赋值给他)
obj.props.children = JSON.parse(JSON.stringify(arg));
}
return obj;
}
}
class ReactDOM {
static handChild(children, newEle) {
if (typeof children === 'object') {
//->当前唯一的子节点是一个新元素(需要重新调用RENDER创建一个新的元素,把元素放在newEle中)
ReactDOM.render(children, newEle);
} else {
//->当前唯一的子节点是一个文本
newEle.appendChild(document.createTextNode(children));
}
}
static render(objJSX, container, callback) {
let {type, props} = objJSX,
newEle = document.createElement(type);
//=>给先创建的元素设置各种属性
for (let key in props) {
if (!props.hasOwnProperty(key)) continue;
//=>CHILDREN
if (key === 'children') {
let children = props['children'];
if (children instanceof Array) {
//=>有多个子节点(遍历一个个来)
children.forEach(itemChild => {
ReactDOM.handChild(itemChild, newEle);
});
continue;
}
//=>只有一个子节点
ReactDOM.handChild(children, newEle);
continue;
}
//=>CLASS
if (key === 'className') {
newEle.setAttribute('class', props['className']);
continue;
}
//=>STYLE 把PROPS中STYLE的值依次遍历,并且设置给元素
if (key === 'style') {
for (let key in props['style']) {
if (props['style'].hasOwnProperty(key)) {
newEle['style'][key] = props['style'][key];
}
}
continue;
}
//...
//->一般情况下把PROPS中的属性直接赋值给元素即可(例如:props={id:'box'} => newEle.setAttribute('id':'box') ...)
newEle.setAttribute(key, props[key]);
}
container.appendChild(newEle);
callback && callback();
}
}
let styleObj = {color: 'red'};
ReactDOM.render(<div id="box"
className="box"
style={styleObj}>
<h2 className="title">javascript和数据结构</h2>
<ul className="newsItem">
<li key='1'>中序遍历的算法原理</li>
<li key='2'>二叉树算法原理及代码实现</li>
<li key='3'></li>
</ul>
</div>, window.root);
复制代码