通过断点调试,探索ReactDOM.createRoot及返回的render方法执行过程

113 阅读1分钟

1. 创建调试源码项目

1.1 拉取React源码, 并创建调试项目,参考调试源码官方方式

1.2 删除调试项目多余的代码,将index.js作为入口改成如下,并在ReactDOM.createRoot处打断点

import React, {useState} from 'react';
import ReactDOM from 'react-dom/client';

const App = () => {
	const [num, setNum] = useState(100);
	return <div onClickCapture={() => setNum(num + 1)}>{num}</div>;
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);

image.png

图1-2-1

2. 启动调试源码项目,并断点调试

2.1 ReactDOM.createRoot方法,创建FiberRootNode, hostRootFiber

image.png

图2-1-1

断点进入到ReactDOM.createRoot内部,通过createFiberRoot方法,创建了root(对应下图FiberRootNode), uninitializedFiber(对应下图hostRootFiber)。并将root.current指向uninitializedFiberuninitializedFiber.stateNode指向root。如下图的对应关系。 接下来执行initializeUpdateQueue方法,初始化了uninitializedFiberupdateQueue属性。

image.png

图2-1-2

2.2 root.render方法调用过程

2.2.1 编译阶段生成的运行时代码转换

如下图,<App />函数组件,由react-scripts内部的@babel/plugin-transform-react-jsx插件在编译阶段转换成jsxDEV方法自执行函数。(由于CRA内部使用的webpack, 融入了一些webpack编译代码)

/*#__PURE__*/ (0, react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(
        App,
        {},
        void 0,
        false,
        {
                fileName: _jsxFileName,
                lineNumber: 11,
                columnNumber: 5
        },
        undefined
)

image.png

图2-2-1

查看打包源文件,如下图。可以看出,jsxDEV方法对应的是isValidElementType方法

image.png

图2-2-2

断点进入isValidElementType方法,参数type为App函数编译后的结果

image.png

图2-2-3

最后转换成ReactElement类型

image.png

图2-2-4

2.2.2 root.render方法执行

接下来进入root.render内部,执行updateContainer方法

image.png

图2-2-5

updateContainer方法内部完成了创建update并插入到current(即hostRootFiber),执行调度函数scheduleUpdateOnFiber,接下来便进入到调度阶段。

image.png

图2-2-6