> 文章列表 > React源码解析之createElement和render方法

React源码解析之createElement和render方法

React源码解析之createElement和render方法

参考资料

请注意,这是React16.8的源码解析,当然他完全可以作为你阅读源码的参考,他还没有落后。

Step1

开始之前,要先了解一个知识点⬇️
我们都知道,要在JSX中写React语法,那为什么不能在js文件中写呢?也可以,但是你要使用相关的Babel转一下react语法,转成JS认识的语法。

换句话说,必须得有Babel将JSX转成JS,你的代码才能正常运行。

Step2

看一下这段代码

const element = <h1 title="foo">Hello</h1>

JS肯定无法识别这句话,但是用到Step1中的Babel转一下,JS就能识别了,那么转成了什么样子呢?让我们打印一下element⬇️
React源码解析之createElement和render方法
没错,变成了一个对象,记住这个type和props

你可以把type理解成标签名,props理解成这个node的所有属性

要注意:这个对象并不是Babel生成的,是React.createElement的方法生成的。
Babel只是告诉node要把这个react语法通过React.createElement方法转成js能识别的对象(这是我猜的)

Step3

让我们来重写一下精简版的React的createElement方法

function createTextElement(text) {return {type: "TEXT_ELEMENT",props: {nodeValue: text,children: []}};
}function createElement(type, props, ...children) {return {type,props: {...props,children: children.map((chil) =>typeof chil === "object" ? chil : createTextElement(chil))}};
}

这里的文本(TEXT_ELEMENT)对象好像和现在的略有不同(我没有看到现在React的TEXT_ELEMENT),不过它也不影响源码阅读。

让我们看几个dom节点生成了怎样的对象吧⬇️

const element = <h1 title="foo">Hello</h1>
⬇️⬇️⬇️
const element = React.createElement("h1",{ title: "foo" },"Hello"
)
⬇️⬇️⬇️
const element = {type: "h1",props: {title: "foo",children: "Hello",},
}
const element = (<div id="foo"><a>bar</a><b /></div>
)
⬇️⬇️⬇️
const element = React.createElement("div",{ id: "foo" },React.createElement("a", null, "bar"),React.createElement("b")
)
⬇️⬇️⬇️
const element = {type: "div",props: {title: "id",children: [{type:"a",props:{children:"bar"}},{type:"b",props:{}},],},
}

Step4

接下来就是render了,将渲染完的对象element,渲染成真正的dom节点⬇️。
让我们重写一下精简版的ReactDom的render的方法,在18的版本中移除了它,也可以继续参考。

function render(element, container) {const { type, props } = element;// 根据type创建节点const dom =type !== "TEXT_ELEMENT"? document.createElement(type): document.createTextNode("");// 将属性添加到节点Object.keys(props).filter((key) => key !== "children").forEach((name) => (dom[name] = props[name]));// 递归增加子节点props.children.forEach((chil) => render(chil, dom));// 添加节点container.appendChild(dom);
}

Step5

接下来,你只需要像初始化react项目时,把你的App挂载到root上就可以了。

const element = (<div style="background: salmon"><h1>Hello World</h1><h2 style="text-align:right">from Didact</h2></div>
);const container = document.getElementById("root");
Lee.render(element, container);

你可以看一下这个sandbox来验证你的学习成果。
这是我的完整练习代码⬇️

function createTextElement(text) {return {type: "TEXT_ELEMENT",props: {nodeValue: text,children: []}};
}function createElement(type, props, ...children) {return {type,props: {...props,children: children.map((chil) =>typeof chil === "object" ? chil : createTextElement(chil))}};
}function render(element, container) {const { type, props } = element;const dom =type !== "TEXT_ELEMENT"? document.createElement(type): document.createTextNode("");Object.keys(props).filter((key) => key !== "children").forEach((name) => (dom[name] = props[name]));props.children.forEach((chil) => render(chil, dom));container.appendChild(dom);
}const Lee = {render,createElement
};/ @jsx Lee.createElement */
const element = (<div style="background: salmon"><h1>Hello World</h1><h2 style="text-align:right">from Didact</h2></div>
);const container = document.getElementById("root");
Lee.render(element, container);