首先我们来看一个最简单的hello world例子
function App() {
return (
hello world
);
}
ReactDOM.render( , document.getElementById("root"));
在babel中转化为如下代码
function App() {
return React.createElement(
"p",
{ className: "title" },
"hello world"
);
}
ReactDOM.render(React.createElement(APP, null), document.getElementById("root"));
打印App(),返回的对象就是一个虚拟DOM
实现React.createElement
通过上文,我们可以知道jsx本质是个语法糖。jsx片段会被转译成用React.createElement
方法包裹的代码。所以第一步,我们来实现这个React.createElement
方法
function createElement(tag, attrs, ...children){
return {
tag,
attrs,
children
}
}
第一个参数是DOM节点的标签名
第二个参数是一个对象,里面保存了所有的属性
从第三个参数开始,就是它的子节点。
我们的createElement方法返回的对象记录了这个DOM节点所有的信息,换言之,通过它我们就可以生成真正的DOM,这个记录信息的对象我们称之为虚拟DOM。
实现ReactDOM.render
我们来看这段代码
ReactDOM.render(
React.createElement("p", { className: "title" }, "hello world"),
document.getElementById("root")
)
所以render
的第一个参数实际上接受的是createElement返回的对象,也就是虚拟DOM
而第二个参数则是挂载的目标DOM
我们来实现这个render方法
function render(vnode, container) {
//当vnode为字符串时,渲染结果是一段文本
if (typeof vnode === "string") {
const textNode = document.createTextNode(vnode)
return container.appendChild(textNode)
}
const dom = document.createElement(vnode.tag)
//设置属性
if (vnode.attrs) {
Object.keys(vnode.attrs).forEach(key => {
const value = vnode.attrs[key]
dom.setAttribute(key, value)
})
}
//递归渲染子节点
vnode.children.forEach(child=> render(child, dom))
return container.appendChild(dom)
}
参考资料
从零开始实现一个React(一):JSX和虚拟DOM