Build your own React_2 render函数
Posted 一只前端小马甲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_2 render函数相关的知识,希望对你有一定的参考价值。
前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。
我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。
假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。
你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。
从零开始重写React框架,我们需要遵循以下步骤:
步骤二:render 函数
接下来,我们来实现一个自己的ReactDOM.render函数。目前只考虑向DOM中添加节点的情况,以后再考虑更新和删除。
function render(element, container){
// TODO create dom nodes
}
const Didact = {
// createElement,
render,
}
/** @jsx Didact.createElement */
// const element = (
// <div id="foo">
// <a>bar</a>
// <b />
// </div>
// )
// const container = document.getElementById("root")
Didact.render(element, container)
我们根据传入的标签名称生成一个DOM节点,之后将节点添加至容器中。
function render(element, container){
const dom = document.createElement(element.type)
container.appendChild(dom)
}
对于子元素递归采用相同的处理方式。
function render(element, container){
// const dom = document.createElement(element.type)
element.props.children.forEach(child=>
render(child, dom)
)
// container.appendChild(dom)
}
对于文字类型元素的处理方式是:如果元素type属性为TEXT_ELEMENT,我们创建一个文字节点,而不是普通节点。
// function render(element, container){
const dom = element.type ==="TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(element.type)
// element.props.children.forEach(child=>
// render(child, dom)
// )
// container.appendChild(dom)
//}
最后一件事情就是将React元素的props属性赋给生成的dom节点。
// function render(element, container){
// const dom = element.type === "TEXT_ELEMENT"
// ? document.createTextNode("")
// : document.createElement(element.type)
const isProperty = key => key !== "children"
Object.keys(element.props)
.filter(isProperty)
.forEach(name=> {
dom[name] = element.props[name]
})
// element.props.children.forEach(child=>
// }
这就是render函数的所有内容,我们现在拥有了将JSX渲染为DOM的库。
function createElement(type, props, ...children){
return {
type,
props: {
...props,
children: children.map(child=>
typeof child === 'object'
? child
: createTextElement(child)
)
},
}
}
function createTextElement(text){
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: []
}
}
}
function render(element, container){
const dom =
element.type === "TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(element.type)
const isProperty = key => key !== "children"
Object.keys(element.props)
.filter(isProperty)
.forEach(name=>{
dom[name] = element.props[name]
})
element.props.children.forEach(child=>
render(child, dom)
)
container.appendChild(dom)
}
const Didact = {
createElement,
render
}
/** @jsx Didact.createElement */
const element = (
<div id="foo">
<a>Bar</a>
<b />
</div>
)
const container = document.getElementById("root")
Didact.render(element, container)
总结
步骤一中,作者详解介绍了通过createElement函数将React元素转化JS对象,该对象是React元素和实际DOM节点之间的中间形态。
步骤二则展示了,如何将这个中间形态转化为DOM节点——通过render函数。步骤一得到的JS对象、根DOM节点被作为参数传出名为render的函数。render函数首先将JS递归转化为DOM树,每个节点可能是文字类型也可能是普通DOM节点,最后将生成的DOM树添加至根DOM节点上。至此,我们已经能清楚的知道一个React元素是如何被转化为实际的DOM节点。
上一篇传送门:Build your own React_1 createElement函数
下一篇传送门:Build your own React_3 并发模式
以上是关于Build your own React_2 render函数的主要内容,如果未能解决你的问题,请参考以下文章
Build your own React_4 理解React纤维