概要
本文是作者在读懂了大神Rodrigo Pombo
的《Build your own React》 源码后,加上了自己的理解,以及做了少量修改后的实现,在这里再次感谢大神!🙏🏻
核心代码 200+ 🎉
fiber 架构的 react 🔥
通俗易懂,对标全网最简单的 react 实现 😍
构建工具选用 parcel,号称零配置
从零到一的实现一个 react
每篇文章在最后,都会附上当前章节源码 🌐
github 源码
创建一个空项目 创建项目并初始化package.json
1 2 mkdir Didactnpm init -y
安装 parcel 1 npm i -D parcel-bundler@^1.12 .5
为了跟着教程走不会因版本问题报错,本文接下来的所有 npm 依赖都将带上版本号
新增 index.html 模板 添加 html 代码:
根节点 root
内联的方式引入 一个index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="utf-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1" /> <meta name ="theme-color" content ="#000000" /> <title > Didact</title > </head > <body > <div id ="root" > </div > </body > <script src ="./index.js" > </script > </html >
添加入口 index.js index.js
里面是一段 JSX 代码
增加 script 命令 修改 package.json
1 2 3 "scripts": { + "start": "parcel index.html", },
执行 npm start
,访问http://localhost:1234/
, 会看到控制台打印"成功启动"
如何处理 JSX 什么是 jsx? 如下就是一段 JSX 代码, 具体请参考
1 2 3 4 5 6 7 const profile = ( <div className ="profile" > <span className ="profile-title" > title</span > <h3 className ="profile-content" > content</h3 > 我是一段文本 </div > );
接下来要将上面 JSX
代码转化为下面的数据结构
来描述
1 2 3 4 5 6 7 8 9 10 11 12 const profile = { type : "div" , props : { className : "profile" , children : [ {type : 'span' , props : {…}}, {type : 'h3' , props : {…}}, "我是一段文本" ], }, }
安装 babel 处理 JSX 我们使用@babel/preset-react
来转化 jsx,但同时需安装它所需要依赖——babel-core
,所以整体安装命令如下:
1 npm i -D @babel/preset-react@^7.17 .12 babel-core@^7.0 .0 -bridge.0
然后再根目录添加.babelrc
文件:
1 2 3 4 5 6 7 8 9 10 11 { "presets" : [ [ "@babel/preset-react" , { "pragma" : "Didact.createElement" } ] ] }
注意:我们在上面设置了pragma
属性,它指定了babel
通过调用Didact.createElement
来递归JSX,从而生成上面的数据结构。
测试 JSX 的转换 将 index.js 的console打印改为上面的那段 JSX
1 2 3 4 5 6 7 8 9 10 11 - console.log('成功启动'); + const profile = ( + <div className="profile"> + <span className="profile-title">title</span> + <h3 className="profile-content">content</h3> + 我是一段文本 + </div> + ); + console.log('profile: ', profile);
打开控制台,会看到如下错误提示:
1 2 3 4 5 Uncaught ReferenceError : Didact is not defined at Object .parcelRequire .index .js (index.js :29 :3 ) at newRequire (Didact .e31bb0bc .js :47 :24 ) at Didact .e31bb0bc .js :81 :7 at Didact .e31bb0bc .js :120 :3
点击进入第一行错误定位,会跳转到源码出错的地方—— “Didact.createElement 未定义”,因为我们还未实现Didact.createElement
,所以因找不到该函数而报错。
但在实现createElement
方法前,我们先看看babel是如何处理jsx的。
babel转换jsx的过程 babel调用Didact.createElement
转换jsx的过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var profile = Didact .createElement ( "div" , { className : "profile" }, Didact .createElement ( "span" , { className : "profile-title" }, "title" ), Didact .createElement ( "h3" , { className : "profile-content" }, "content" ), "我是一段文本" ); console .log ('profile: ' , profile);
从上面代码可以看出,@babel/preset-react
做了两件事情:
将 JSX 代码转换成了参数,type, props, ...children
将上面的参数传递给 Didact.createElement,并执行该函数
1 2 3 4 5 6 7 8 9 10 11 12 Didact .createElement ( type, [props], [...children] )
实现 createElement 方法 很简单,让Didact.createElement
返回一个含有 children 的树状结构,就实现了createElement
在index.js中添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + function createElement(type, props, ...children) { + return { + type, + props: { + ...props, + ...children, + } + }; + } + const Didact = { + createElement, + }; const profile = ( <div className="profile"> <span className="profile-title">title</span> <h3 className="profile-content">content</h3> 我是一段文本 </div> ); console.log('profile: ', profile);
这样就实现了createElement
方法。
但通过console打印发现,children
中的所有元素,除了文本节点
是string
其它节点都是对象
。
这里将文本节点也统一处理成对象,这样后面会少了很多if、else
的判断。
将文本节点构建为type:'TEXT_ELEMENT'
的对象,修改代码:
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 function createElement(type, props, ...children) { return { type, props: { ...props, - ...children, + children: children.map(child => + typeof child === "object" ? child : createTextElement(child) + ) } }; } + function createTextElement(text) { + return { + type: "TEXT_ELEMENT", + props: { + nodeValue: text, + children: [] + } + }; + } const Didact = { createElement, };
这样我们就彻底完成了createElement
方法
本章源码