自己造一个ReactDOM
source link: https://segmentfault.com/a/1190000040999929
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
大家好,我卡颂。
React
可以看作是三部分的组合:
scheduler
,调度器,用于调度任务reconciler
,协调器,用于计算任务造成的副作用renderer
,渲染器,用于在宿主环境执行副作用
这三者都是独立的包,我们项目里引入的ReactDOM
可以看作是以下三部分代码打包而成:
scheduler
的主要逻辑reconciler
部分逻辑ReactDOM renderer
的主要逻辑
本文会教你如何基于官方的reconciler
,实现迷你ReactDOM
。
项目初始化
通过CRA
建立项目(或用已有项目):
create-react-app xxx
新建customRenderer.js
,引入react-reconciler
并完成初始化:
// 本文使用的reconciler版本是0.26.2 import ReactReconciler from 'react-reconciler'; const hostConfig = {}; const ReactReconcilerInst = ReactReconciler(hostConfig);
其中hostConfig
就是宿主环境的配置项。
最后,customRenderer.js
导出一个包含render
方法的对象:
export default { render: (reactElement, domElement, callback) => { // 创建根节点 if (!domElement._rootContainer) { domElement._rootContainer = ReactReconcilerInst.createContainer(domElement, false); } return ReactReconcilerInst.updateContainer(reactElement, domElement._rootContainer, null, callback); } };
在项目入口文件,将ReactDOM
换成我们实现的CustomRenderer
:
import ReactDOM from 'react-dom'; import CustomRenderer from './customRenderer'; // 替换ReactDOM CustomRenderer.render( <App />, document.getElementById('root') );
实现ReactDOM
接下来我们实现hostConfig
配置,首先填充空函数避免应用报错:
const hostConfig = { supportsMutation: true, getRootHostContext() {}, getChildHostContext() {}, prepareForCommit() {}, resetAfterCommit() {}, shouldSetTextContent() {}, createInstance() {}, createTextInstance() {}, appendInitialChild() {}, finalizeInitialChildren() {}, clearContainer() {}, appendInitialChild() {}, appendChild() {}, appendChildToContainer() {}, prepareUpdate() {}, commitUpdate() {}, commitTextUpdate() {}, removeChild() {} }
注意这里唯一一个Boolean
类型的配置项supportsMutation
,他表示宿主环境的API
支持mutation
。
这是DOM API
的工作方式,比如element.appendChild
、element.removeChild
。如果是Native
环境则不是这种工作方式。
接下来我们来实现这些API
。
实现API
这些API
可以分为如下几类。
初始化环境信息
getRootHostContext
与getChildHostContext
用于初始化上下文信息。
生成DOM节点
createInstance
用于创建DOM节点createTextInstance
用于创建文本节点
可以将createTextInstance
实现如下:
createTextInstance: (text) => { return document.createTextNode(text); }
关键逻辑的判断
shouldSetTextContent
用于判断组件的children
是否是文本节点
,实现如下:
shouldSetTextContent: (_, props) => { return typeof props.children === 'string' || typeof props.children === 'number'; },
DOM操作
appendInitialChild
用于插入DOM
节点,实现如下:
appendInitialChild: (parent, child) => { parent.appendChild(child); },
commitTextUpdate
用于改变文本节点
,实现如下:
commitTextUpdate(textInstance, oldText, newText) { textInstance.text = newText; },
removeChild
用于删除子节点,实现如下:
removeChild(parentInstance, child) { parentInstance.removeChild(child); }
当实现了所有API
后,页面就能正常渲染了:
完整实现的Demo
地址见:完整Demo地址
经过本文的学习,我们实现了一个简易ReactDOM
。
如果你想在任何可以绘制UI
的环境使用React
,都可以利用react-reconciler
实现该环境下的React
。
比如,Introduction To React Native Renderers教你如何在Native
环境实现React
。
欢迎加入人类高质量前端框架群,带飞
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK