自己造一个ReactDOM

开发 前端
本文会教你如何基于官方的reconciler,实现迷你ReactDOM。如果你想在任何可以绘制UI的环境使用React,都可以利用react-reconciler实现该环境下的React。

[[436840]]

大家好,我卡颂。

React可以看作是三部分的组合:

  • scheduler,调度器,用于调度任务
  • reconciler,协调器,用于计算任务造成的副作用
  • renderer,渲染器,用于在宿主环境执行副作用

这三者都是独立的包,我们项目里引入的ReactDOM可以看作是以下三部分代码打包而成:

  • scheduler的主要逻辑
  • reconciler部分逻辑
  • ReactDOM renderer的主要逻辑

本文会教你如何基于官方的reconciler,实现迷你ReactDOM。

本文参考Hello World Custom React Renderer[1]

项目初始化

通过CRA建立项目(或用已有项目):

  1. create-react-app xxx 

新建customRenderer.js,引入react-reconciler并完成初始化:

  1. // 本文使用的reconciler版本是0.26.2 
  2. import ReactReconciler from 'react-reconciler'
  3.  
  4. const hostConfig = {}; 
  5. const ReactReconcilerInst = ReactReconciler(hostConfig); 

其中hostConfig就是宿主环境的配置项。

最后,customRenderer.js导出一个包含render方法的对象:

  1. export default { 
  2.   render: (reactElement, domElement, callback) => { 
  3.     // 创建根节点 
  4.     if (!domElement._rootContainer) { 
  5.       domElement._rootContainer = ReactReconcilerInst.createContainer(domElement, false); 
  6.     } 
  7.  
  8.     return ReactReconcilerInst.updateContainer(reactElement, domElement._rootContainer, null, callback); 
  9.   } 
  10. }; 

在项目入口文件,将ReactDOM换成我们实现的CustomRenderer:

  1. import ReactDOM from 'react-dom'
  2. import CustomRenderer from './customRenderer'
  3.  
  4. // 替换ReactDOM 
  5. CustomRenderer.render( 
  6.   <App />, 
  7.   document.getElementById('root'
  8. ); 

实现ReactDOM接下来我们实现hostConfig配置,首先填充空函数避免应用报错:

  1. const hostConfig = { 
  2.   supportsMutation: true
  3.   getRootHostContext() {}, 
  4.   getChildHostContext() {}, 
  5.   prepareForCommit() {}, 
  6.   resetAfterCommit() {}, 
  7.   shouldSetTextContent() {}, 
  8.   createInstance() {}, 
  9.   createTextInstance() {}, 
  10.   appendInitialChild() {}, 
  11.   finalizeInitialChildren() {}, 
  12.   clearContainer() {}, 
  13.   appendInitialChild() {}, 
  14.   appendChild() {}, 
  15.   appendChildToContainer() {}, 
  16.   prepareUpdate() {}, 
  17.   commitUpdate() {}, 
  18.   commitTextUpdate() {}, 
  19.   removeChild() {} 

注意这里唯一一个Boolean类型的配置项supportsMutation,他表示宿主环境的API支持mutation。

这是DOM API的工作方式,比如element.appendChild、element.removeChild。如果是Native环境则不是这种工作方式。

接下来我们来实现这些API。

实现API

这些API可以分为如下几类。

初始化环境信息

getRootHostContext与getChildHostContext用于初始化上下文信息。

生成DOM节点

  • createInstance用于创建DOM节点
  • createTextInstance用于创建文本节点

可以将createTextInstance实现如下:

  1. createTextInstance: (text) => { 
  2.   return document.createTextNode(text); 

关键逻辑的判断

shouldSetTextContent用于判断组件的children是否是文本节点,实现如下:

  1. shouldSetTextContent: (_, props) => { 
  2.     return typeof props.children === 'string' || typeof props.children === 'number'
  3. }, 

DOM操作

appendInitialChild用于插入DOM节点,实现如下:

  1. appendInitialChild: (parent, child) => { 
  2.   parent.appendChild(child); 
  3. }, 

commitTextUpdate用于改变文本节点,实现如下:

  1. commitTextUpdate(textInstance, oldText, newText) { 
  2.   textInstance.text = newText; 
  3. }, 

removeChild用于删除子节点,实现如下:

  1. removeChild(parentInstance, child) { 
  2.   parentInstance.removeChild(child); 

当实现了所有API后,页面就能正常渲染了:

完整实现的Demo地址见:完整Demo地址[2]

总结

经过本文的学习,我们实现了一个简易ReactDOM。

如果你想在任何可以绘制UI的环境使用React,都可以利用react-reconciler实现该环境下的React。

比如,Introduction To React Native Renderers[3]教你如何在Native环境实现React。

参考资料

[1]Hello World Custom React Renderer:

https://agent-hunt.medium.com/hello-world-custom-react-renderer-9a95b7cd04bc

[2]完整Demo地址:

https://codesandbox.io/s/quiet-feather-05gvk?file=/src/index.js

[3]Introduction To React Native Renderers:

https://agent-hunt.medium.com/introduction-to-react-native-renderers-aka-react-native-is-the-java-and-react-native-renderers-are-828a0022f433

 

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2022-11-29 12:53:36

机器人物理MIT

2017-03-02 13:31:02

监控系统

2022-04-08 08:48:16

线上事故日志订阅者

2019-12-26 14:30:54

LinuxPython硬件

2011-02-28 09:22:47

SQLite记账簿

2022-08-09 10:00:57

ViteTypeScripVue3

2021-06-26 16:24:21

Linux命令系统

2012-07-19 13:49:20

2024-01-08 13:47:00

代码分析工具

2015-07-03 11:27:30

程序员自己神器

2017-02-14 10:20:43

Java Class解析器

2023-08-31 22:05:02

SAN环境存储

2021-04-25 08:58:00

Go拍照云盘

2020-06-18 10:36:12

GitHub代码开发者

2022-06-10 14:52:46

开源项目字节跳动

2019-09-02 13:57:07

Helm Chart工具Kubernetes

2021-08-04 11:55:45

Python天气查询PySide2

2015-06-02 09:51:40

iOS网络请求封装接口

2020-08-24 07:33:20

CSS框架 SASS

2022-03-17 15:34:47

printf日志
点赞
收藏

51CTO技术栈公众号