自己造一个ReactDOM

大家好,我卡颂。
React可以看作是三部分的组合:

  • scheduler,调度器,用于调度任务
  • reconciler,协调器,用于计算任务造成的副作用
  • renderer,渲染器,用于在宿主环境执行副作用
这三者都是独立的包,我们项目里引入的ReactDOM可以看作是以下三部分代码打包而成:
  • scheduler的主要逻辑
  • reconciler部分逻辑
  • ReactDOM renderer的主要逻辑
本文会教你如何基于官方的reconciler,实现迷你ReactDOM
本文参考 Hello World Custom React Renderer
项目初始化 通过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( , 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.appendChildelement.removeChild。如果是Native环境则不是这种工作方式。
接下来我们来实现这些API
实现API 这些API可以分为如下几类。
初始化环境信息
getRootHostContextgetChildHostContext用于初始化上下文信息。
生成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后,页面就能正常渲染了:
自己造一个ReactDOM
文章图片

完整实现的Demo地址见:完整Demo地址
总结 经过本文的学习,我们实现了一个简易ReactDOM
如果你想在任何可以绘制UI的环境使用React,都可以利用react-reconciler实现该环境下的React
比如,Introduction To React Native Renderers教你如何在Native环境实现React
【自己造一个ReactDOM】欢迎加入人类高质量前端框架群,带飞

    推荐阅读