前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >自己造一个ReactDOM

自己造一个ReactDOM

作者头像
公众号@魔术师卡颂
发布2021-11-23 09:55:30
8800
发布2021-11-23 09:55:30
举报
文章被收录于专栏:魔术师卡颂

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

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

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

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

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

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

项目初始化

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

代码语言:javascript
复制
create-react-app xxx

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

代码语言:javascript
复制
// 本文使用的reconciler版本是0.26.2
import ReactReconciler from 'react-reconciler';

const hostConfig = {};
const ReactReconcilerInst = ReactReconciler(hostConfig);

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

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

代码语言:javascript
复制
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

代码语言:javascript
复制
import ReactDOM from 'react-dom';
import CustomRenderer from './customRenderer';

// 替换ReactDOM
CustomRenderer.render(
  <App />,
  document.getElementById('root')
);

实现ReactDOM

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

代码语言:javascript
复制
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实现如下:

代码语言:javascript
复制
createTextInstance: (text) => {
  return document.createTextNode(text);
}

关键逻辑的判断

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

代码语言:javascript
复制
shouldSetTextContent: (_, props) => {
    return typeof props.children === 'string' || typeof props.children === 'number';
},

DOM操作

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

代码语言:javascript
复制
appendInitialChild: (parent, child) => {
  parent.appendChild(child);
},

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

代码语言:javascript
复制
commitTextUpdate(textInstance, oldText, newText) {
  textInstance.text = newText;
},

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

代码语言:javascript
复制
removeChild(parentInstance, child) {
  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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 魔术师卡颂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目初始化
  • 实现ReactDOM
  • 实现API
    • 初始化环境信息
      • 生成DOM节点
        • 关键逻辑的判断
          • DOM操作
          • 总结
            • 参考资料
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档