- 作者:老汪软件技巧
- 发表时间:2024-09-06 04:01
- 浏览量:
前言
第4节我们说到,react在createContainer的时候会初始化一个更新队列,赋值给fiber的updateQueue,然后在updateContainerImpl时会创建一个更新(update),把element放入这个更新的payload,再把这个更新(update)通过enqueueUpdate方法放入更新队列中,在后续调度中我们就会通过processUpdateQueue方法去消费这个更新队列中的更新
所以这个更新机制是如何设计的呢?
目录:packages/react-reconciler/src/ReactFiberClassUpdateQueue.js
Update
react中update代表一个更新,它的数据结构是
export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3;
export type Update<State> = {
// 更新的优先级标识
lane: Lane,
// 更新标识
tag: 0 | 1 | 2 | 3,
// 更新时候的动作或者值
payload: any,
// 更新之后执行的回调
callback: (() => mixed) | null,
// 指向下一个Update
next: Update<State> | null,
};
UpdateQueue
消费Update的结构就是UpdateQueue,我们可以理解成一个更新队列,它的核心数据结构是
export type UpdateQueue<State> = {
shared: SharedQueue<State>,
// ...
};
export type SharedQueue<State> = {
// 待执行的Update
pending: Update<State> | null,
// 对应的优先级
lanes: Lanes,
// ...
};
所以UpdateQueue.shared.pending保存的就是当前待执行的Update,UpdateQueue和Update的关系是
创建Update
react中使用createUpdate方法创建一个Update,入参为lane,返回值为Update
export function createUpdate(lane: Lane): Update {
const update: Update = {
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
初始化更新队列(创建更新队列)
react中使用initializeUpdateQueue去创建一个新的更新队列,入参为一个Fiber,该方法会初始化一个更新队列,然后将这个更新队列赋值给Fiber的updateQueue属性
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
// 本次更新前该Fiber节点的state
baseState: fiber.memoizedState,
// 上次渲染时遗留下来的低优先级任务会组成一个链表,该字段指向到该链表的头节点
firstBaseUpdate: null,
// 该字段指向到该链表的尾节点
lastBaseUpdate: null,
shared: {
// 本次渲染要执行更新的环形链表放在这
pending: null,
lanes: NoLanes,
hiddenCallbacks: null,
},
callbacks: null,
};
fiber.updateQueue = queue;
}
插入队列
react中使用enqueueUpdate将一个Update插入到一个更新队列UpdateQueue中
export function enqueueUpdate<State>(
fiber: Fiber,
update: Update<State>,
lane: Lane,
): FiberRoot | null {
// 获取fiber上的updateQueue
const updateQueue = fiber.updateQueue;
// 当前updateQueue的shared对象,pending属性就为当前Update
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
if (isUnsafeClassRenderPhaseUpdate(fiber)) {
// Update之间关联的核心逻辑在下面:
// pending代表当前的Update
const pending = sharedQueue.pending;
if (pending === null) {
// 如果pending不存在,则是第一次,直接指向自己
// a -> a
update.next = update;
} else {
// 将传入update指向当前update的下一个,将当前update指向传入update
// 如果传入b: b -> a -> b
// 如果再传入c:c -> a -> b -> c
update.next = pending.next;
pending.next = update;
}
// 将队列当前update改成传入udate
sharedQueue.pending = update;
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
其实本质上就是构造成一个环形链表,假设updateQueue.shared.pending就是一个 c -> a -> b -> c 的环形链表,那么updateQueue.shared.pending.next就指向第一个update(a)
消费更新队列
在react中使用processUpdateQueue来消费当前更新队列中的update
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
// 当前更新队列
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
let firstBaseUpdate = queue.firstBaseUpdate;
let lastBaseUpdate = queue.lastBaseUpdate;
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
// 环形链表的最后一个
const lastPendingUpdate = pendingQueue;
// 环形链表的第一个
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
if (lastBaseUpdate === null) {
firstBaseUpdate = firstPendingUpdate;
} else {
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
const current = workInProgress.alternate;
if (current !== null) {
const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
// 如果currentLastBaseUpdate和待更新的lastBaseUpdate不一样就把firstPendingUpdate和lastPendingUpdate拼进去
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
if (firstBaseUpdate !== null) {
// 当前state值
let newState = queue.baseState;
let newLanes: Lanes = NoLanes;
// 下次渲染时的state值
let newBaseState = null;
// 下面的两个指针用来存放一个新的低优先级的更新链表
let newFirstBaseUpdate = null; // 新的更新链表的头指针
let newLastBaseUpdate: null | Update<State> = null; // 新的更新链表的尾指针
// 拿到第一个update开始执行
let update: Update<State> = firstBaseUpdate;
do {
// 当前update的优先级
const updateLane = removeLanes(update.lane, OffscreenLane);
const isHiddenUpdate = updateLane !== update.lane;
// 判断是否需要跳过这个update,updateLane不是renderLanes的子集就跳过
const shouldSkipUpdate = isHiddenUpdate
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
: !isSubsetOfLanes(renderLanes, updateLane);
// 如果这个update应该跳过
if (shouldSkipUpdate) {
// 复制这个update
const clone: Update<State> = {
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
/** 将该update放到新的队列中,为了保证链式操作的连续性,
* 下面else逻辑中已经可以执行的update,也放到这个队列中
*/
// 低优先级更新列表不存在,clone就是第一个节点
if (newLastBaseUpdate === null) {
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
// 低优先级更新列表存证,拼接起来
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
newLanes = mergeLanes(newLanes, updateLane);
} else {
// 这个update的优先级足够,没有被跳过
if (updateLane !== NoLane && updateLane === peekEntangledActionLane()) {
didReadFromEntangledAsyncAction = true;
}
/**
* 若存储低优先级的更新链表不为空,则为了操作的完整性
* 即使当前update会执行,也将当前的update节点也拼接到后面
*/
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
lane: NoLane,
tag: update.tag,
payload: update.payload,
callback: null,
next: null,
};
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// 执行update,update中的payload转换成新的state
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
const callback = update.callback;
if (callback !== null) {
workInProgress.flags |= Callback;
if (isHiddenUpdate) {
workInProgress.flags |= Visibility;
}
const callbacks = queue.callbacks;
if (callbacks === null) {
queue.callbacks = [callback];
} else {
callbacks.push(callback);
}
}
}
// 执行完update之后指向下一个update
update = update.next;
if (update === null) {
pendingQueue = queue.shared.pending;
if (pendingQueue === null) {
break;
} else {
// An update was scheduled from inside a reducer. Add the new
// pending updates to the end of the list and keep processing.
const lastPendingUpdate = pendingQueue;
// Intentionally unsound. Pending updates form a circular list, but we
// unravel them when transferring them to the base queue.
const firstPendingUpdate =
((lastPendingUpdate.next: any): Update<State>);
lastPendingUpdate.next = null;
update = firstPendingUpdate;
queue.lastBaseUpdate = lastPendingUpdate;
queue.shared.pending = null;
}
}
} while (true);
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
// 将新的state赋值给baseState
queue.baseState = ((newBaseState: any): State);
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
if (firstBaseUpdate === null) {
queue.shared.lanes = NoLanes;
}
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
// 将新的state挂载到memoizedState上
workInProgress.memoizedState = newState;
}
}