前情提要,也可以当作是一个小目录吧,我会在阅读完后把自己的理解做一个输出,励志让所有有 React 基本功的同学都能看懂。
我们将从头开始重写 React。一步步。遵循真实 React 代码的架构,但没有所有优化和非必要功能。
如果您阅读过我之前的任何一篇“构建自己的 React”文章,不同之处在于这篇文章基于 React 16.8,因此我们现在可以使用 hooks 并删除所有与类相关的代码。
您可以在Didact repo上找到包含旧博客文章和代码的历史记录。还有一个涵盖相同内容的演讲。但这是一个独立的帖子。
从头开始,这些是我们将一一添加到我们的 React 版本中的所有内容:
- 第一步:
createElement
功能
- 第二步:
render
函数
- 第三步:并发模式
- 第四步:纤维
- 第五步:渲染和提交阶段
- 第六步:和解
- 第七步:功能组件
- 第八步:钩子
from https://pomb.us/build-your-own-react/
Step I: The createElement Function
分析一下一个 React 元素转换成 DOM 的过程:程序员使用 React.createElement 并传入对应的 type 、props、children 参数后会生成一个 React Element,后由 ReactDOM 核心 API render 方法去将它转换成 DOM。知道了上述的生成逻辑就好办了。
第一个阶段:创建一个我们自己的 createElement 方法。
首先我们要了解 React 元素是如何转换成 DOM 节点的?我目前的理解其实 React 元素本质上就是一个对象。第一步就是创建一个类似于 React 元素的一个对象
用 JS 来创建一个 DOM元素
下一步我们要了解抛开 React 我们要如何用 JS 代码创建一个 DOM。
初始化一个 HTML 并声明一个 root 根节点来放页面的内容。
1 2 3 4 5 6 7 8 9 10 11 12
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"></div> </body> </html>
|
获取 root 节点,用 DOM API 写入一个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const container = document.getElementById("root")
const node = document.createElement("h1")
node["title"] = '这是一个 h1 的标题'
const text = document.createTextNode("")
text["nodeValue"] = "1111"
node.appendChild(text)
container.appendChild(node)
|
了解 React.createElement 传递参数和执行后产出的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const element = { type: "h1", props: { title: "foo", children: "Hello", }, }
const element = React.createElement( "h1", { title: "foo" }, "Hello" )
const element = React.createElement( "div", { id: "foo" }, React.createElement("a", null, "bar"), React.createElement("b") )
|
创建我们自己的 createElement func
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
const createTextElement = text => { return { type: 'TEXT_ELEMENT', props: { nodeValue: text, children: [], }, }; };
const createElement = (type, props, ...children) => { return { type, props: { ...props, children: children.map(child => { typeof child === 'object' ? child : createTextElement(child); }), }, }; };
const MyReact = { createElement, };
|
使用我们的 createElement 来创建 MyReact 元素
1 2 3 4 5 6
| const element = MyReact.createElement( "div", { id: "foo" }, MyReact.createElement("a", null, "bar"), MyReact.createElement("b") )
|
告诉 Babel 用我们指定的 createElement
但是我们仍然想在这里使用 JSX。我们如何告诉 babel 使用 MyReact 的createElement
而不是 React 的?
1 2 3 4 5 6 7 8 9
| const element = ( <div id="foo"> <a>bar</a> <b /> </div> ) const container = document.getElementById("root") ReactDOM.render(element, container)
|