You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionrender(element,container){// TODO create dom nodes}constmyReactRender={
createElement,
render,};constelement=myReactRender.createElement("div",{id: "foo"},myReactRender.createElement("a",null,"bar"),myReactRender.createElement("b"),);constcontainer=document.getElementById("root");myReactRender.render(element,container);
这篇文章带大家实现一个简单的render函数,在此之前,你需要对jsx语法和DOM元素的工作原理有基本了解
我们将实现这个React渲染函数,只有三行代码:
注意知道的是:在这里我们只实现函数本身,不会关心jsx是如何编译成render函数的,因为那是编译器(比如Babel)的工作,jsx在编译的时候,通过某些构建工具(比如Babel)转换为js,转换过程很简单:用createElement函数代替我们定义的内容,同时将标签名、props、子元素作为参数传递给createElement函数
让我们删除所有React的代码,用普通的JavaScript代替它:
在上面的代码中,第一行是用jsx定义的元素,实际上它并不是有效的JavaScript,所以为了使用有效的js,首先我们需要用createElement替换掉jsx:
然后再把上面代码中的createElement函数转换成普通的element对象,可以写成下面这样:
可以看到,createElement函数的作用是根据其参数创建一个对象,对象有两个属性:type和props
我们需要替换的另一部分React代码是对ReactDOM.render的调用:
现在,我们有了和开始几行代码一样的功能,但是没有使用React
接下来,让我们开始编写自己的createElement函数:
正如我们在前面的步骤中所看到的:element是一个具有type和props的对象。我们的函数唯一需要做的就是创建这个对象:
我们对props使用扩展操作符,对children参数使用rest语法,这样可以保证子元素props将始终是一个数组,例如:
createElement("div")
返回:createElement("div", null, a)
返回:createElement("div", null, a, b)
返回:需要注意的是:children还可以包含
string
或者number
等基本类型
的值。因此,我们需要先写一个函数区分基本类型的值和对象类型的值,然后把所有不是对象类型的值封装一下,并为它们创建一个特殊的类型:TEXT_ELEMENT
需要知道的是,在react源码中,当没有子元素时,React不会封装原始值、也不会创建空数组,而我们这样做的原因,仅仅是因为我太懒,这样写起来简单,并且对于我们简易版的render函数,我更喜欢简单的代码、而不是性能代码。所以不用去追求细节(躺平就完了)
然后仍然回到React的createElement函数:
为了替换它,我们给自己的函数起个新的名字:myReactRender
目前已经实现了createElement函数,接下来需要实现render函数:
接下来需要处理render函数:
TEXT_ELEMENT
,我们将创建一个文本节点最后一件事,就是将props的每一个值分配给创建的dom:
最后一步,如果我们仍然想在这里使用jsx语法,我们如何告诉Babel使用myReactRender的渲染函数而不是React的?
我们只需要添加这样的注释就好了,当Babel编译jsx时,它将使用我们定义的函数:
就是这样,现在我们有了一个可以将JSX渲染到DOM的库,完整代码如下:
截止目前,我们用40行代码实现了react的render函数。
The text was updated successfully, but these errors were encountered: