「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
前一篇文章已经梳理了源码的阅读顺序,我们顺着 render -> legacyRenderSubtreeIntoContainer -> updateContainer 这个链路,更为详细地分析一下代码的功能。
以下代码分析过程的某些描述可能与你对代码的理解有出入,这是因为为了简化对原理的理解,本文省略了某些实现的细节,真实的实现会比本文描述更复杂。比如说,某处构造 root 是调了createContainer函数,但其实该函数只是对createFiberRoot函数的封装,本文会直接说某处构造 root 调了 createFiberRoot
render
看一下 render函数的签名
function render (
element: React$Element<any>,
container: Container,
callback: ?Function,
) {
// ...
}
调用方式
ReactDOM.render(
<React.StrictMode>
<App></App>
</React.StrictMode>,
document.getElementById('root')
)
element 就是我们写的 React 组件,container 是渲染根组件的容器,一般传入一个 dom 对象。callback 函数用于在 container 被更新的时候执行。
render 函数除去 if (__DEV__) 语句块和错误校验语句块!isValidContainerLegacy(container)),有效代码只剩下一个 return legacyRenderSubtreeIntoContainer(null, element, container, false, callback)。
legacyRenderSubtreeIntoContainer
从函数名来看,这应该是用于实现将虚拟 DOM 树渲染到容器(真实 DOM) 上的功能。它的函数签名如下:
// 将虚拟dom渲染到真实 dom
function legacyRenderSubtreeIntoContainer (
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
) {
// ...
}
它做了以下几件事:
- 令
root = container._reactRootContainer,若不存在则创建,用legacyCreateRootFromDOMContainer函数创建 - 重写 callback 函数,令其被执行时有
getPublicRootInstance(root)作为实参传入 callback - 执行
updateContainer(children, fiberRoot, parentComponent, callback), 如果是虚拟dom树是第一次挂载到 container 下,则不直接调用updateContainer,而是执行flushSync,在flushSync的回调中执行updateContainer
它的返回值是一个 getPublicRootInstance(root),其实现如下:
export function getPublicRootInstance(
container: OpaqueRoot,
): React$Component<any, any> | PublicInstance | null {
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
switch (containerFiber.child.tag) {
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
return containerFiber.child.stateNode;
}
}
返回的是一个 container.current.child.stateNode
legacyCreateRootFromDOMContainer
创建 root 的过程到底做了什么?
function legacyCreateRootFromDOMContainer (
container: Container,
forceHydrate: boolean,
): FiberRoot {
// ...
}
函数作用如下:
- 如果 forceHydrate 为 false,则清除 container 已存在的子元素
- 用
createFiberRoot函数构造 root。 - 执行 markContainerAsRoot(root.current, container)
- 对 container 监听所有支持的事件,调了
listenToAllSupportedEvents方法
在 createFiberRoot中,主要做了以下事情:
root = new FiberRootNode(...)root.current = createHostRootFiber(...)root.current.stateNode = root(形成了一个循环结构)- 初始化
root.current.memoizedState - 初始化更新队列:
initializeUpdateQueue(root.current),事实逻辑如下:
root.current.updateQueue = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
interleaved: null,
lanes: NoLanes,
},
effects: null,
}