React触发更新及更新流程

1,230 阅读7分钟

更新逻辑

  1. Fiber上创建Update
    1. 设置相关属性laneeventTimepayloadcallback
    2. 函数:createUpdate->enqueueUpdate
  2. 通过Scheduler调度安排渲染。
    1. 确认渲染任务performConcurrentWorkOnRootperformSyncWorkOnRoot的优先级、执行时机。
    2. 函数:scheduleUpdateOnFiber-> ensureRootIsScheduled

数据结构

Class Component

UpdateQueueUpdate节点构成的循环链表
SharedQueue是链表尾结点。

type Update<State> = {
  // 事件时间,后面会删除,将在根目录中存储转换->事件时间的映射.
  eventTime: number, 
  // 优先级
  lane: Lane,
   // 更新类型 
  // 对应 UpdateState、ReplaceState、ForceUpdate、CaptureUpdate
  tag: 0 | 1 | 2 | 3,
  // newState 或者 newState函数
  payload: any,
  callback: (() => mixed) | null,
  // 下一个update
  next: Update<State> | null,
};


type UpdateQueue<State> = {
  // update list 循环链表
  shared: SharedQueue<State>, 
  // 下面3个值,都是 中断保护现场 的状态。
  // 更新被高优先级打断时,打断前的值。 
  baseState: State, 
  firstBaseUpdate: Update<State> | null,
  lastBaseUpdate: Update<State> | null,
  // 副作用
  effects: Array<Update<State>> | null,
};


type SharedQueue<State> = {
  // UpdateQueue的尾结点,是循环链表
  pending: Update<State> | null,
  // UpdateQueue的优先级,会merge更新队列的lane
  lanes: Lanes,
  interleaved: Update<State> | null,
};

type Fiber = {
  // state保存的是值
  memoizedState: any
  // UpdateQueue
  updateQueue:UpdateQueue
}

Function Component

函数组件的状态是Hooks管理的,所以每个Hooks都有自己的UpdateQueue

type Update<S, A> = {
  lane: Lane,  // 优先级
  action: A,   // newState 或者 newState函数
  hasEagerState: boolean, // state 优化
  eagerState: S | null,   // state 优化
  next: Update<S, A>,     // 下一个update
};

type UpdateQueue<S, A> = {
  // update list 尾结点,是循环链表
  pending: Update<S, A> | null,
  // UpdateQueue的优先级,会merge更新队列的lane
  lanes: Lanes,
  // 对应的dispatch函数
  dispatch: (A => mixed) | null,
  // 每次使用useReducer传入的,重新赋值reducer函数。每次使用都是最新的。
  lastRenderedReducer: ((S, A) => S) | null,
  // 上一次调度更新hook计算出的state。
  lastRenderedState: S | null,
  interleaved: Update<S, A> | null,
};

type Hook = {
  // state
  memoizedState: any,
  // UpdateQueue
  queue: any, 
  // 下一个hook
  next: Hook | null,
  // 下面2个值,都是 中断保护现场 的状态。
  // 更新被高优先级打断时,打断前的值。 
  baseState: any,
  // 打断前的UpdateQueue
  baseQueue: Update<any, any> | null,
};

type Fiber = {
  // state保存的是hook链表的尾结点。一个循环链表
  memoizedState: Hook
  // Hook有自己的updateQueue,
  // fiber的UpdateQueue暂时无用。
}

触发更新的方式

ReactDOM.render

Update的数据结构与ClassCompoent相同。

updateContainer

ReactDOM.renderReactDOM.createRoot().render都是调用updateContainer

//ReactDOM.render(element)和ReactDOM.createRoot(element).render都是调用updateContainer。
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): Lane {
  // current是FiberRoot
  const current = container.current;
  // 时间
  const eventTime = requestEventTime();
  // 优先级
  const lane = requestUpdateLane(current);

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }
   // 创建update,ClassComponent的。
  const update = createUpdate(eventTime, lane);
  // FiberRoot的payload是所挂载的DOM节点
  // React DevTools目前依赖于这个属性
  update.payload = {element};
   
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    update.callback = callback;
  }
  // 将Update添加到Fiber.updateQueue 队尾。
  enqueueUpdate(current, update, lane);
  // 调度更新任务
  const root = scheduleUpdateOnFiber(current, lane, eventTime);
  // 调度优先级相关
  if (root !== null) {
    entangleTransitions(root, current, lane);
  }
  return lane;
}

this.setState -- Class Component

ClassComponentstate操作,都由一个classComponentUpdater对象提供。
在组件创建时,添加到实例中instance.updater = classComponentUpdater

enqueueSetState

// this.setState 实际调用函数
enqueueSetState(inst, payload, callback) {
  // 获取组件对应的fiber
  const fiber = getInstance(inst);
  const eventTime = requestEventTime();
  const lane = requestUpdateLane(fiber);
  // 创建Update。 赋值lane、eventTime、payload、callback
  const update = createUpdate(eventTime, lane);
  update.payload = payload;
  update.callback = callback;
  // 将Update添加到Fiber.updateQueue 队尾。
  enqueueUpdate(fiber, update, lane);
  // 调度更新任务
  const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
  // 调度优先级相关
  if (root !== null) {
    entangleTransitions(root, fiber, lane);
  }
}

this.fourceUpdate -- Class Component

this.setState基本一致。
只有update数据稍有改动

// this.fourceUpdate 实际调用函数
// 不同点:不能传入payload
enqueueForceUpdate(inst, callback) {
  const fiber = getInstance(inst);
  const eventTime = requestEventTime();
  const lane = requestUpdateLane(fiber);
  
  // 不同点:payload为null.   (创建时默认为null)
  const update = createUpdate(eventTime, lane);
  // 不同点:更新tag ForceUpdate  
   update.tag = ForceUpdate;
  
  enqueueUpdate(fiber, update, lane);
  const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
  if (root !== null) {
    entangleTransitions(root, fiber, lane);
  }
}

useReducer -- Function Component

新版本将 useReduceruseState区分开了,因为旧版useReducer有一些BUG。

dispatchReducerAction

function dispatchReducerAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  const lane = requestUpdateLane(fiber);
  // update
  const update: Update<S, A> = {
    lane, // 优先级
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };
   // fiber是否正在render 
  // 正在render是指,组件函数执行时触发 更新(即触发此函数)
  if (isRenderPhaseUpdate(fiber)) {
    // eg: <Test/> 组件内直接 dispatch(1), 这时组件render会立即触发此函数。
    // (优化手段)
    // 在组件函数执行完毕后,
    // 重复调用此组件函数。
    // 直到组件调用时,没有触发更新。
    // 若触发次数过多,进行报错。
    // 处理逻辑在:函数组件渲染函数 renderWithHooks 中。
    
    // 将Update添加到Fiber.memoizedState(Hook).queue 队尾。
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    // 将Update添加到Fiber.memoizedState(Hook).queue 队尾。
    enqueueUpdate(fiber, queue, update, lane);
    const eventTime = requestEventTime();
    
    // 调度更新任务
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
    // 调度优先级相关
    if (root !== null) {
      entangleTransitionUpdate(root, queue, lane);
    }
  }
}

useState -- Function Component

新版本将 useReduceruseState区分开了,因为旧版useReducer有一些BUG。

Update 数据结构

// eg: dispatch( n => n+1 )
const update: Update<S, A> = {
  lane,  // 优先级
  action,// dispatch传参 eg: n => n+1
  hasEagerState: false, // 
  eagerState: null, // 
  next: (null: any),//next update
};

dispatchSetState


function dispatchSetState<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  const lane = requestUpdateLane(fiber);
  const update: Update<S, A> = {
    lane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };

  // fiber是否正在render 
  // 正在render是指,组件函数执行时触发 更新(即触发此函数)
  if (isRenderPhaseUpdate(fiber)) {
    // eg: <Test/> 组件内直接 dispatch(1), 这时组件render会立即触发此函数。
    // (优化手段)
    // 在组件函数执行完毕后,
    // 重复调用此组件函数。
    // 直到组件调用时,没有触发更新。
    // 若触发次数过多,进行报错。
    // 处理逻辑在:函数组件渲染函数 renderWithHooks 中。
    
    // 将Update添加到Fiber.memoizedState(Hook).queue 队尾。
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    // 将Update添加到Fiber.memoizedState(Hook).queue 队尾。
    enqueueUpdate(fiber, queue, update, lane);
   
    const alternate = fiber.alternate;
    if (
      // 说明fiber上面没update,这是本轮第一个update!
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
      // 直接计算state,因为是第一个update,不用考虑优先级的其他逻辑。
      // 这是优化,尽量减少状态调度。
      
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {
        let prevDispatcher;
        try {
          // 上一次useReducer的state。
          const currentState: S = (queue.lastRenderedState: any);
          // 计算本次的state
          const eagerState = lastRenderedReducer(currentState, action);
          // 记录是否 执行过 此优化逻辑。
          update.hasEagerState = true;
          update.eagerState = eagerState;
                      
          // 新旧state相同,不更新。
          if (is(eagerState, currentState)) { return; }
        } catch (error) {
          // Suppress the error. It will throw again in the render phase.
        }
      }
    }
    const eventTime = requestEventTime();
    // 调度更新任务
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
    // 调度优先级相关
    if (root !== null) {
      entangleTransitionUpdate(root, queue, lane);
    }
  }
}

并发模式下的state更新公式

baseState + Update1 + Updata2 +(优先级足够的Update) = newState

  • baseState:上一轮更新后的state值,若被高优先级打断,则值为中断发生前一个计算出的值。
  • 会过滤掉优先级不足的Update

安排渲染

根据update,和当前react运行状态,调度渲染任务。

scheduleUpdateOnFiber

调度fiber上的update

export function scheduleUpdateOnFiber(
  fiber: Fiber,
  lane: Lane,
  eventTime: number,
): FiberRoot | null {
   // 将lane合并到,此fiber到Root的路径上的所有Fiber中。  
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  if (root === null) { return null; }
  // 标记根有一个挂起的更新
  markRootUpdated(root, lane, eventTime);

  if (
    // 正在渲染,且root和正在work的root相同。
    (executionContext & RenderContext) !== NoLanes &&
    root === workInProgressRoot
  ) {
    // 共用当前render。merge lane。
    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(
      workInProgressRootRenderPhaseUpdatedLanes,
      lane,
    );
  } else {
    // 调度一个渲染任务 
    ensureRootIsScheduled(root, eventTime);
    if (
      lane === SyncLane && //同步优先级
      executionContext === NoContext && // 执行上下文,无任务
      (fiber.mode & ConcurrentMode) === NoMode && // 不是并发模式
    ) {
      // 兼容 leacy 模式的正确渲染。
      // eg: 在setTimeout,接口等异步,调用setState,会走此逻辑。
      // 直接进行渲染。
      flushSyncCallbacksOnlyInLegacyMode();
    }
  }
  return root;
}

ensureRootIsScheduled

root调度performSyncWorkOnRoot(渲染)

function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
  // 保存,调度单元,scheduler所创建的task对象,与并发模式的调度有关。
  const existingCallbackNode = root.callbackNode;
   // 是否有 lane 饿死,标记为过期。
  markStarvedLanesAsExpired(root, currentTime);
   // 找到优先级最高的lane
  const nextLanes = getNextLanes(
    root,
    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
  );
   
  // 无任务
  if (nextLanes === NoLanes) {
    if (existingCallbackNode !== null) {
      cancelCallback(existingCallbackNode);
    }
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    return;
  }
   // 根据lane,获取优先级
  const newCallbackPriority = getHighestPriorityLane(nextLanes);

  const existingCallbackPriority = root.callbackPriority;
  if (
     // 检查是否有一个正在执行的的任务,优先级相同复用它。
    existingCallbackPriority === newCallbackPriority &&
  ) {
    return;
  }
  
  if (existingCallbackNode != null) {
    //取消现有的回调。我们将在下面安排一个新的。
    cancelCallback(existingCallbackNode);
  }

  // Schedule 一个新的 callback.
  let newCallbackNode;
  // 同步优先级
  if (newCallbackPriority === SyncLane) {
    // 将performSyncWorkOnRoot放入 同步执行回调队列 SyncCallbacks
    if (root.tag === LegacyRoot) {
      // legacy模式,调度渲染performSyncWorkOnRoot
      scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
    } else {
      // 并发模式,调度渲染performSyncWorkOnRoot
      scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
    }
    // 是否支持微任务
    // 当前是同步优先级,立即执行渲染
    if (supportsMicrotasks) {
      // flushSyncCallbacks,即立刻执行performSyncWorkOnRoot
      scheduleMicrotask(flushSyncCallbacks);
    } else {
      // flushSyncCallbacks,即立刻执行performSyncWorkOnRoot
      scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
    }
    newCallbackNode = null;
  } else {
    // 并发模式 异步优先级。 
    let schedulerPriorityLevel;
    // lane =>  scheduler Priority
    switch (lanesToEventPriority(nextLanes)) {
      case DiscreteEventPriority:
        schedulerPriorityLevel = ImmediateSchedulerPriority;
        break;
      case ContinuousEventPriority:
        schedulerPriorityLevel = UserBlockingSchedulerPriority;
        break;
      case DefaultEventPriority:
        schedulerPriorityLevel = NormalSchedulerPriority;
        break;
      case IdleEventPriority:
        schedulerPriorityLevel = IdleSchedulerPriority;
        break;
      default:
        schedulerPriorityLevel = NormalSchedulerPriority;
        break;
    }
    // 保存,调度单元,scheduler所创建的task对象
    newCallbackNode = scheduleCallback(
      schedulerPriorityLevel,
      performConcurrentWorkOnRoot.bind(null, root),
    );
  }
  // 保存,调度优先级
  root.callbackPriority = newCallbackPriority;
   // 保存,调度单元,scheduler所创建的task对象
  root.callbackNode = newCallbackNode;
}

performSyncWorkOnRoot

此函数将会 diff渲染DOM
此文不细说具体怎么渲染。

综述

写作时间:2021-01-16
react版本:17.0.3