react-dom后续updateContainer部分。阅读React包的源码版本为16.8.6。
在上一章节中我们看到了react-dom中render函数的逻辑是给传入的React组件创建了一个fiberRoot对象,用于标识它是整个应用的起点,上面拥有很多应用更新相关的表示符。然后创建对应的fiber给fiberRoot节点,fiber对象是每一个ReactElement都拥有的节点,它标识了更新时间的一些信息,props和state的一些信息,以及相关联的节点信息。ReactElement彼此是通过一个单向链表的数据结构联系在一起的。
这一章我们接着legacyRenderSubtreeIntoContainer函数创建完fiber相关对象的部分,查看接下来updateContainer相关的逻辑。我们先回顾一下这部分的代码。
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
// 是否复用dom节点,服务端渲染调用
forceHydrate: boolean,
callback: ?Function,
) {
// ...省略创建fiber节点相关部分逻辑
// 初次使用render不存在root节点
if (!root) {
// ...省略创建fiber节点相关部分逻辑
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root._internalRoot;
// 有无callback 逻辑同上
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
我们先看callback部分的处理,在render函数中callback是传入的第三个参数,根据react文档,该回调将在组件被渲染或更新之后被执行,并且在非箭头函数的情况下,该回调的this指向render渲染的那个组件。我们先来回顾下这个入参的使用方式。
const instance = render(
<Hello text="123" />,
document.getElementById("page"),
function () {
console.log(this) }
);
console.log(instance);
/*
this === instance === Hello
Hello {
isMounted: (...)
replaceState: (...)
props: {text: "123"}
context: {}
refs: {}
updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
_reactInternalFiber: FiberNode {tag: 1, key: null, stateNode: Hello, elementType: ƒ, type: ƒ, …}
_reactInternalInstance: {_processChildContext: ƒ}
state: null
__proto__: Component
}
*/
在上述代码中使用render函数时,传入了一个匿名函数作为render的第三个入参,并打印了this,然后将render函数的返回值赋予了instance变量并打印出来。我们可以看到,输出的是一个对象信息,其实使用过react测试相关诸如react-test-renderer等框架的,应该对这个instance比较熟悉。它标志了一个由fiberRoot开始的完整的组件信息。
现在回到源码,我们可以看到我们在调用render时候,callback的this和render返回的组件instance信息都是由getPublicRootInstance创建的。react将我们传入的callback赋值给了变量originalCallback,然后声明一个新的callback,新的callback创建了一个instance,然后用call让originalCallback的this指向它,把它传入到了updateContainer的callback参数中。
至于getPublicRootInstance如何创建一个Instance的细节代码与主流程牵扯不大,这边就跳过。只要知道该函数根据fiberRoot提供了一个Instance信息对象即可。接着我们可以看到,无论是否是初次使用render函数(初次调用render函数不存在root节点),legacyRenderSubtreeIntoContainer都调用了updateContainer方法,区别就是初次使用render的时候,updateContainer是在unbatchedUpdates方法回调中使用的。unbatchedUpdates做的事情实际就是在render初次调用的时候,不用去批量更新updateContainer,这个函数做的事情仅仅是改变了几个标志符,然后立即更新了CallbackQueue,我们也略过这部分逻辑,重点来看一下updateContainer相关的部分。
function updateContainer(
element: ReactNodeList,
container: OpaqueRoot, // root
parentComponent: ?React$Component<any, any>, // 根节点是null
callback: ?Function,
): ExpirationTime {
// fiberRoot的current为fiberRoot的fiber对象
const current = container.current;
// 获得当前时间到js加载完时间的时间差值
const currentTime = requ

本文深入探讨React源码16.8.6中的update和updateQueue,涉及updateContainer、FiberNode、任务调度等内容。文章详细介绍了在React组件渲染或更新后如何处理回调函数、创建FiberNode、更新标识符以及任务队列的构建过程,为理解React更新机制奠定基础。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



