image.png用jsx写的代码,babel会将其转化为react的虚拟dom,如下图:
可以看到,转化后,是用
React.createElement
方法来生成dom的,它大约有3(更多)个参数: type,props, children
,props是这个元素的详情
,children可以有多个,代表这个元素的子元素
, 那么它的执行结果又是什么呢?image.png
上图打印出的对象就是react元素,他是一个类似真实dom的对象,type
是标签名,props是其属性,像attribute,class,id等都存在于此,props中还有一个比较特殊的属性children
,它是子元素集合(只有一个子元素(即文本元素)时,它的类型为string
)。
接下来,我们就来写一个简易版的react+react-dom
// 首先,完成react
function createElement(type, config, ...children) {
// 将children使用rest参数的方法合并起来
let props = { ...config, children };
return { type, props }; // 此时,就生成虚拟元素
}
export default {
createElement
};
接下来是重头戏,react-dom
function render(element, parent) {
if (typeof element === "string") {
//文本元素,直接生成,随后插入父元素
element = document.createTextNode(element);
}
let { type, props } = element; //element是一个虚拟dom对象
if (typeof type === "string") {
element = document.createElement(type);
for (let propName in props) {
if (propName === "style") { // 添加样式
Object.entries(props.style).forEach(([attr, value]) => {
element.style[attr] = value;
});
} else if (propName === "className") { // 添加class
element.className = props.className;
} else if (propName === "children") {
props.children.forEach(child => { // 生成child并且插入父级
return render(child, element);
});
}
}
}
parent.appendChild(element);
}
export default {
render
};
业务代码:
//index.js
...
let elm = React.createElement(
"h1",
{ className: "title", style: { backgroundColor: "green" } },
"hello",
React.createElement("span", null, "world")
);
ReactDOM.render(elm, document.getElementById("root"));
image.png
正常输出了!
以上,只是演示了普通的html元素,接下来,再兼容
函数组件
和类组件
。
//index.js
function Welcome(props) { //函数式组件
return React.createElement(
"h1",
{ className: "title", style: { backgroundColor: "green" } },
props.name,
React.createElement("span", null, props.age)
);
}
// class Welcome extends React.Component { //类组件
// render() {
// return React.createElement(
// "h1",
// { className: "title", style: { backgroundColor: "green" } },
// this.props.name,
// React.createElement("span", null, this.props.age)
// );
// }
// }
let element = React.createElement(Welcome, { name: "lc", age: 18 });
ReactDOM.render(element, document.getElementById("root"));
修改react/index.js
,以支持类组件
function createElement(type, config, ...children) {
let props = { ...config, children };
let element = { type, props };
return element;
}
class Component {
static isReactComponent = true;
constructor(props) {
this.props = props;
}
}
export default {
createElement,
Component,
};
再来修改react-dom,以支持解析函数式和类组件
// react-dom/index.js
function render(element, parent) {
let { type, props } = element; //element是一个虚拟dom对象
if (typeof element === "string" || typeof element === "number") { //文本元素
//文本元素,直接生成,随后插入父元素
element = document.createTextNode(element);
} else if (type.isReactComponent) {
//类组件
element = new type(props).render();
type = element.type;
props = element.props;
} else if (typeof type === "function") { // 函数组件
element = type(props);
type = element.type;
props = element.props;
}
if (typeof type === "string") { //
element = document.createElement(type);
for (let propName in props) {
if (propName === "style") {
Object.entries(props.style).forEach(([attr, value]) => {
element.style[attr] = value;
});
} else if (propName === "className") {
element.className = props.className;
} else if (propName === "children") {
props.children.forEach(child => {
return render(child, element);
});
}else{
dom.setAttribute(propName, props[propName])
}
}
}
parent.appendChild(element);
}
export default {
render
};
网友评论