概览
在ReactDOMRoot的render和unmount方法中均调用了updateContainerImpl方法,该方法是React中更新容器的核心方法,负责创建和调度更新。
源码分析
updateContainerImpl
updateContainerImpl方法源码实现如下:
js
function updateContainerImpl(
rootFiber, //根Fiber节点
lane, // 更新优先级
element, //要渲染的React元素
container, // Fiber根节点容器
parentComponent, // 父组件
callback // 更新完成后的回调函数
) {
// 获取子树的上下文,会返回一个空对象
parentComponent = getContextForSubtree(parentComponent);
// 若Fiber根节点容器上下文为空,则设置其为获取到的上下文(空对象);否则设置待处理的上下文为空对象
null === container.context
? (container.context = parentComponent)
: (container.pendingContext = parentComponent);
// 根据优先级车道创键更新对象
container = createUpdate(lane);
// 将要渲染的React元素作为更新对像的载荷
container.payload = { element: element };
// 若回调未定义,则设为null,否则保留原值
callback = void 0 === callback ? null : callback;
// 若回调不为null,则将其设为更新对象的callback属性值
null !== callback && (container.callback = callback);
// 将更新对象添加到Fiber节点的更新队列,并获取FiberRoot
element = enqueueUpdate(rootFiber, container, lane);
// 判断 若返回的更新对象不为null,则调度更新
null !== element &&
(scheduleUpdateOnFiber(element, rootFiber, lane),
entangleTransitions(element, rootFiber, lane));
}
createUpdate
createUpdate接受一个参数车道优先级(lane),返回一个更新对象,其对象类型为Update,Update会被放入更新队列中等待调度执行,其优先级为参数lane,tag为UpdateState:0,其实现如下:
js
function createUpdate(lane){
return {
lane: lane, // 更新优先级
tag: UpdateState, // 其值为1 ,表示更新类型
payload: null, // 更新载荷
callback: null, // 回调函数
next: null // 指向下一个更新
}
}
enqueueUpdate
enqueueUpdate方法用于将更新对象加入Fiber节点的更新队列,其源码实现如下:
js
function enqueueUpdate(fiber, update, lane) {
// 获取Fiber节点上的更新队列updateQueue
var updateQueue = fiber.updateQueue;
// 若更新队列为空,则返回null,此时Fiber节点已被卸载
if (null === updateQueue) return null;
// 获取共享队列
updateQueue = updateQueue.shared;
// 检查是否是渲染更新阶段
if (0 !== (executionContext & 2)) {
// 获取共享队列的最后一个节点
var pending = updateQueue.pending;
// 判断最后一个节点是否为空
null === pending
? (update.next = update) // 将更新对象update的next指向自身
: ((update.next = pending.next), (pending.next = update)); //否则将更新对象的next指向最后一个节点的next,并且将最后一个节点的next指向更新对象,即将更新对象插入到环形链表的末尾
// 更新pending指针
updateQueue.pending = update;
// 从Fiber节点中获取FiberRoot
update = getRootForUpdatedFiber(fiber);
// 标记fiber节点中的优先级
markUpdateLaneFromFiberToRoot(fiber, null, lane);
// 最后返回FiberRoot
return update;
}
// 若不是渲染更新阶段,则依次执行enqueueUpdate$1,getRootForUpdatedFiber,最后返回getRootForUpdatedFiber的结果FiberRoot
// 调用enqueueUpdate$1方法就是将相关参数放到并发队列concurrentQueues中
enqueueUpdate$1(fiber, updateQueue, update, lane);
return getRootForUpdatedFiber(fiber);
}
getRootForUpdatedFiber
Fiber节点的return始终是指向父节点,因此通过不断追溯return可以找到根节点;
js
function getRootForUpdatedFiber(sourceFiber) {
// 判断某计数器,暂且不提
if (50 < nestedUpdateCount)
throw (
((nestedUpdateCount = 0),
(rootWithNestedUpdates = null),
Error(formatProdErrorMessage(185)))
);
for (var parent = sourceFiber.return; null !== parent; )
(sourceFiber = parent), (parent = sourceFiber.return);
// 判断当前Fiber节点的根节点类型是否为HostRoot,若是,则返回其DOM元素,否则返回null
return 3 === sourceFiber.tag ? sourceFiber.stateNode : null;
}
scheduleUpdateOnFiber
调用enqueueUpdate方法更新相关队列后,会拿到FiberRoot,若FiberRoot不为null,则调用scheduleUpdateOnFiber进行调度更新。
entangleTransitions
entangleTranslations方法用于纠缠过渡更新,主要有3个作用:
- 将当前更新与正在进行的过渡更新纠缠
- 确保过渡更新的正确顺序
- 处理并发模式下的更新冲突