自己造一個ReactDOM
大家好,我卡頌。
React可以看作是三部分的組合:
- scheduler,調(diào)度器,用于調(diào)度任務(wù)
 - reconciler,協(xié)調(diào)器,用于計算任務(wù)造成的副作用
 - renderer,渲染器,用于在宿主環(huán)境執(zhí)行副作用
 
這三者都是獨立的包,我們項目里引入的ReactDOM可以看作是以下三部分代碼打包而成:
- scheduler的主要邏輯
 - reconciler部分邏輯
 - ReactDOM renderer的主要邏輯
 
本文會教你如何基于官方的reconciler,實現(xiàn)迷你ReactDOM。
本文參考Hello World Custom React Renderer[1]
項目初始化
通過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就是宿主環(huán)境的配置項。
最后,customRenderer.js導(dǎo)出一個包含render方法的對象:
- export default {
 - render: (reactElement, domElement, callback) => {
 - // 創(chuàng)建根節(jié)點
 - if (!domElement._rootContainer) {
 - domElement._rootContainer = ReactReconcilerInst.createContainer(domElement, false);
 - }
 - return ReactReconcilerInst.updateContainer(reactElement, domElement._rootContainer, null, callback);
 - }
 - };
 
在項目入口文件,將ReactDOM換成我們實現(xiàn)的CustomRenderer:
- import ReactDOM from 'react-dom';
 - import CustomRenderer from './customRenderer';
 - // 替換ReactDOM
 - CustomRenderer.render(
 - <App />,
 - document.getElementById('root')
 - );
 
實現(xiàn)ReactDOM接下來我們實現(xiàn)hostConfig配置,首先填充空函數(shù)避免應(yīng)用報錯:
- const hostConfig = {
 - supportsMutation: true,
 - getRootHostContext() {},
 - getChildHostContext() {},
 - prepareForCommit() {},
 - resetAfterCommit() {},
 - shouldSetTextContent() {},
 - createInstance() {},
 - createTextInstance() {},
 - appendInitialChild() {},
 - finalizeInitialChildren() {},
 - clearContainer() {},
 - appendInitialChild() {},
 - appendChild() {},
 - appendChildToContainer() {},
 - prepareUpdate() {},
 - commitUpdate() {},
 - commitTextUpdate() {},
 - removeChild() {}
 - }
 
注意這里唯一一個Boolean類型的配置項supportsMutation,他表示宿主環(huán)境的API支持mutation。
這是DOM API的工作方式,比如element.appendChild、element.removeChild。如果是Native環(huán)境則不是這種工作方式。
接下來我們來實現(xiàn)這些API。
實現(xiàn)API
這些API可以分為如下幾類。
初始化環(huán)境信息
getRootHostContext與getChildHostContext用于初始化上下文信息。
生成DOM節(jié)點
- createInstance用于創(chuàng)建DOM節(jié)點
 - createTextInstance用于創(chuàng)建文本節(jié)點
 
可以將createTextInstance實現(xiàn)如下:
- createTextInstance: (text) => {
 - return document.createTextNode(text);
 - }
 
關(guān)鍵邏輯的判斷
shouldSetTextContent用于判斷組件的children是否是文本節(jié)點,實現(xiàn)如下:
- shouldSetTextContent: (_, props) => {
 - return typeof props.children === 'string' || typeof props.children === 'number';
 - },
 
DOM操作
appendInitialChild用于插入DOM節(jié)點,實現(xiàn)如下:
- appendInitialChild: (parent, child) => {
 - parent.appendChild(child);
 - },
 
commitTextUpdate用于改變文本節(jié)點,實現(xiàn)如下:
- commitTextUpdate(textInstance, oldText, newText) {
 - textInstance.text = newText;
 - },
 
removeChild用于刪除子節(jié)點,實現(xiàn)如下:
- removeChild(parentInstance, child) {
 - parentInstance.removeChild(child);
 - }
 
當(dāng)實現(xiàn)了所有API后,頁面就能正常渲染了:
完整實現(xiàn)的Demo地址見:完整Demo地址[2]
總結(jié)
經(jīng)過本文的學(xué)習(xí),我們實現(xiàn)了一個簡易ReactDOM。
如果你想在任何可以繪制UI的環(huán)境使用React,都可以利用react-reconciler實現(xiàn)該環(huán)境下的React。
比如,Introduction To React Native Renderers[3]教你如何在Native環(huán)境實現(xiàn)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
















 
 
 















 
 
 
 