• 作者:老汪软件技巧
  • 发表时间: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的关系是

_react更新机制_react状态更新

创建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;
  }
}