「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
出发点
最近在学习react源码,如果直接去看源码,估计能劝退一大帮人,手敲一遍更能让我了解react是如何工作的。好了,废话不多说,直接开始吧。
实现
我们将分为8步,一步一步的实现一个小型的React
- 实现createElement函数
- 实现render函数
- Currnet Mode模式
- fibers
- render和commit阶段
- 协调器
- function组件
- hooks
最最最最最简单的实现
我们先看一段代码
const element = <h1 title="foo">Hello Word</h1>
const container = document.getElementById("root")
ReactDOM.render(element, container)
首先是创建React element,然后获取id为root的dom节点,最后渲染React element到root节点里。
其中第一行代码JSX转换为js如下,jsx转换为js可以通过babel转换
const element = React.createElement(
"h1",
{ title: "foo" },
"Hello",
)
React.createElement 方法接收type、config、childern参数,返回一个对象,该对象包含:$$typeof、type、key、ref、props、_owner属性,我们可以进行简单的模拟,只需要type和props
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
}
其中type为dom节点的类型,props中是JSX中设置的一些属性,children属性为子节点,可以为string和array。
然后是替换ReactDOM.render(element, container)
const container = document.getElementById("root")
const node = document.createElement(element.type)
node["title"] = element.props.title
const text = document.createTextNode("")
text["nodeValue"] = element.props.children
node.appendChild(text)
container.appendChild(node)
到这里,没有使用react实现了一段代码
实现createElement函数
先来看一段简单的JSX代码和转换成JS后的样子
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")
)
前面我们已经知道React.createElement方法返回的是一个对象,属性type为dom节点的tagName,属性props为dom节点的属性和子节点,下面来实现一下:
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children,
},
}
}
这里的children参数总是为array,用来接收子节点,我们来看些例子:
createElement('div') =>
{
"type": "div",
"props": { "children": [] }
}
createElement("div", null, a) =>
{
"type": "div",
"props": { "children": [a] }
}
createElement("div", null, a, b) =>
{
"type": "div",
"props": { "children": [a, b] }
}
children还可以接收想string或number类型的值,所以继续改进一下createElement方法,当child不是object类型时,就返回type为TEXT_ELEMENT的对象
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: [],
},
}
}
接下来我们可以用自己实现的createElement替换React.createElement
const Didect = {
createElement,
}
const element = Didect.createElement(
'div',
{ id: 'foo' },
Didect.createElement('a', null, 'bar'),
Didect.createElement('b'),
)
const container = document.getElementById("root")
ReactDOM.render(element, container)
总结
我们写的JSX会被转换为React.createElement,该方法会创建一个对象,定义react的Element-TYPE类型,并返回这个对象