React 源碼解析 React 的更新

React 的更新

只解析到創(chuàng)建更新進(jìn)入調(diào)度器

創(chuàng)建更新的方式

  • 初始渲染
    ReactDOM.render
    ReactDOM.hydrate

  • 更新渲染
    setState
    forceUpdate (舍棄)

ReactDOM.render

步驟

  • 創(chuàng)建 ReactRoot
    最頂點(diǎn)的對(duì)象
  • 創(chuàng)建 FiberRoot 和 RootFiber
  • 創(chuàng)建更新 update
    用來(lái)更新調(diào)度, 進(jìn)入調(diào)度后 setState 或 ReactDOM.render 都的調(diào)度器去管理的

初始渲染

ReactDOM.render

ReactDOM.render(<App />, document.getElementById('root'))
<App /> 只是調(diào)用了 createReactElement 生成了ReactElement
ReactDOM.render 把 這個(gè) ReactElement 渲染成 dom tree

初始渲染

在 react-dom/client/ReactDOM.js 文件下, 有 render hydrate 兩個(gè)方法, render 是用在瀏覽器環(huán)境內(nèi)的,hydrate 用在服務(wù)器環(huán)境下,唯一的區(qū)別是調(diào)用 legacyRenderSubtreeIntoContainer 方法傳入的第四個(gè)參數(shù)不同。

const ReactDOM = {
  // ...
  hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
    // TODO: throw or warn if we couldn't hydrate?
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      true,
      callback,
    );
  },

  render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  },
  
  // ...
}
  • legacyRenderSubtreeIntoContainer 中 container 就是首次渲染傳入的<div id="root">, 這個(gè)dom 肯定不存在 _reactRootContainer 屬性,所以 !root 內(nèi)的就是初次渲染的邏輯
  • 而 root 是由首次渲染邏輯時(shí) 由 legacyCreateRootFromDOMContainer 生成的一個(gè) Fiber 對(duì)象。
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
  // ...
 // container 就是首次渲染傳入的<div id="root">, 這個(gè)dom 肯定不存在 _reactRootContainer 屬性,所以 !root 內(nèi)的就是初次渲染的邏輯
 let root: Root = (container._reactRootContainer: any);
  if (!root) {
    // Initial mount 初次渲染,進(jìn)行 new ReactRoot, 創(chuàng)建一個(gè) createFiberRoot 回來(lái)
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    if (typeof callback === 'function') {
      // 回調(diào)封裝
    }
    // Initial mount should not be batched. unbatchedUpdates 批量更新
    DOMRenderer.unbatchedUpdates(() => {
      if (parentComponent != null) { // render, hydrate 傳入的 parentComponent 都是 null
        // 批量更新回調(diào), ReactDOM.render 不執(zhí)行
      } else {
        root.render(children, callback); // 主要執(zhí)行 ReactRoot 實(shí)例的 render
      }
    });
  } else { // 更新渲染的邏輯 }
  return DOMRenderer.getPublicRootInstance(root._internalRoot); // _internalRoot 是 new ReactRoot 生成的 Fiber 對(duì)象
}
  • legacyCreateRootFromDOMContainer 就是把 root dom, container 內(nèi)的其余節(jié)點(diǎn)清空創(chuàng)建一個(gè) new ReactRoot 實(shí)例
function legacyCreateRootFromDOMContainer(
  container: DOMContainer, // ReactDOM.render 傳入的 root
  forceHydrate: boolean, // render: false, hydrate: true, SSR 時(shí)進(jìn)行節(jié)點(diǎn)復(fù)用,也是 render hydrate 唯一的區(qū)別
): Root {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  if (!shouldHydrate) { // false 客戶(hù)端渲染
    let warned = false;
    let rootSibling;
    // 把 root 的所有子節(jié)點(diǎn)刪掉
    while ((rootSibling = container.lastChild)) {
     container.removeChild(rootSibling);
    }
  }
  // Legacy roots are not async by default. root 節(jié)點(diǎn)創(chuàng)建時(shí)同步的,isConcurrent: false
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}
  • ReactRoot 生成實(shí)例創(chuàng)建了一個(gè) root 實(shí)例屬性,root 由 react-reconcile 模塊包里的 createContainer 方法 調(diào)用 createFiberRoot 生成一個(gè) Fiber 對(duì)象,最后掛載到實(shí)例的 _internalRoot 上
function ReactRoot(
  container: Container,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  // 創(chuàng)建一個(gè) createFiberRoot
  const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}
// DOMRenderer.createContainer 
export function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
  • legacyCreateRootFromDOMContainer 最終執(zhí)行的 root.render 就是 new ReactRoot 的原型方法 ReactRoot.prototype.render , 最終是調(diào)用 react-reconcile 模塊包里的 updatecontainer
ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot; // 一個(gè) fiber root
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;

  if (callback !== null) {
    work.then(callback);
  }
  // render 的重點(diǎn)調(diào)用
  DOMRenderer.updateContainer(children, root, null, work._onCommit);
  return work;
};
  • DOMRenderer.updateContainer 中計(jì)算出一個(gè) expirationTime 傳入了 updateContainerAtExpirationTime.
export function updateContainer(
  element: ReactNodeList, // App
  container: OpaqueRoot, // 一個(gè) fiber root
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  const currentTime = requestCurrentTime(); // 創(chuàng)建一個(gè)時(shí)間差
  // 計(jì)算出一個(gè)時(shí)間,ConcurrentMode 會(huì)用到
  const expirationTime = computeExpirationForFiber(currentTime, current);
  // 主要執(zhí)行
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}
  • updateContainerAtExpirationTime 中調(diào)用 scheduleRootUpdate
export function updateContainerAtExpirationTime(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {
  // TODO: If this is a nested container, this won't be the root.
  const current = container.current;

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }

  return scheduleRootUpdate(current, element, expirationTime, callback);
}
  • scheduleRootUpdate 中
  • 使用 createUpdate 創(chuàng)建 update 來(lái)標(biāo)記 react 需要更新的點(diǎn)
  • 設(shè)置完 update 屬性再調(diào)用 enqueueUpdate 把 update 放入更新隊(duì)列里
    react 更新會(huì)在一個(gè)節(jié)點(diǎn)上整體進(jìn)行很多個(gè)更新,這個(gè)更新 queue 就是管理多次更新的作用
  • 最后執(zhí)行 scheduleWork 通知 react 進(jìn)行調(diào)度,根據(jù)任務(wù)的優(yōu)先級(jí)進(jìn)行更新。
function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {

  const update = createUpdate(expirationTime); // 用來(lái)標(biāo)記 react 更新的節(jié)點(diǎn)
  // Caution: React DevTools currently depends on this property
  // being called "element".
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    warningWithoutStack(
      typeof callback === 'function',
      'render(...): Expected the last optional `callback` argument to be a ' +
        'function. Instead received: %s.',
      callback,
    );
    update.callback = callback;
  }
  // 把更新的對(duì)象加入到 fiber 對(duì)象上的 updateQueue 里, 會(huì)有很多更新在一個(gè)節(jié)點(diǎn)上產(chǎn)生
  enqueueUpdate(current, update);
  // 開(kāi)始進(jìn)行調(diào)度
  scheduleWork(current, expirationTime);
  return expirationTime;
}

ReactDOM.render 階段總結(jié)

  • 初次渲染 傳入 APP 組件和 getElementById(root) 執(zhí)行 ReactDOM.render
  • ReactDOM.render 返回并執(zhí)行 legacyRenderSubtreeIntoContainer
    • legacyRenderSubtreeIntoContainer 內(nèi)調(diào)用 legacyCreateRootFromDOMContainer 把返回值掛載到 root 節(jié)點(diǎn)的 _reactRootContainer 屬性上
      • 而 legacyCreateRootFromDOMContainer 把 getElementById(root) 里的子節(jié)點(diǎn)清空,創(chuàng)建并返回 new ReactRoot 給 getElementById(root) 的 _reactRootContainer 屬性上
      • ReactRoot 生成實(shí)例時(shí)調(diào)用 react-reconcile 模塊的 createContainer 傳入 getElementById(root) 執(zhí)行 createFiberRoot 生成一個(gè) FiberRoot 對(duì)象掛載到實(shí)例的 _internalRoot
    • legacyRenderSubtreeIntoContainer 最終調(diào)用 上面生成的 ReactRoot 實(shí)例的 ReactRoot.prototype.render 原型方法
      • ReactRoot.prototype.render 把子節(jié)點(diǎn)和實(shí)例生成的 _internalRoot Fiber 對(duì)象傳入 react-reconcile 模塊的 updateContainer 中
        • 在 updateContainer 中 react 計(jì)算出一個(gè) expirationTime 傳入 updateContainerAtExpirationTime 調(diào)用 scheduleRootUpdate 中做三件事
          1 使用 createUpdate 創(chuàng)建 update 來(lái)標(biāo)記 react 需要更新的點(diǎn)
          2 設(shè)置完 update 屬性再調(diào)用 enqueueUpdate 把 update 放入當(dāng)前節(jié)點(diǎn)樹(shù)整體的更新隊(duì)列里
          3 最后執(zhí)行 scheduleWork 通知 react 進(jìn)行調(diào)度,根據(jù)任務(wù)的優(yōu)先級(jí)進(jìn)行更新。
  • ReactDOM.render 此時(shí)
    • 創(chuàng)建了一個(gè) ReactRoot 對(duì)象掛載到 getElementById(root) 的 _reactRootContainer 屬性上
    • 同時(shí) 在 ReactRoot 實(shí)例 _internalRoot 屬性上生成了 Fiber 對(duì)象
    • 調(diào)用 ReactRoot.prototype.render 執(zhí)行 react-reconcile 模塊的 updateContainer 計(jì)算 expirationTime,通過(guò) expirationTime 來(lái)創(chuàng)建 update 對(duì)象,推入 updateQueue 內(nèi),最后根據(jù)優(yōu)先級(jí)進(jìn)行調(diào)度。

FiberRoot

  • 整個(gè)應(yīng)用的起點(diǎn)
  • 包含了傳入的 getElementById(root)
  • 記錄整個(gè)應(yīng)用更新過(guò)程的各種信息 containerInfo (包含了 root 節(jié)點(diǎn)等信息)

FiberRoot 對(duì)象結(jié)構(gòu)

FiberRoot 是 ReactRoot 生成實(shí)例時(shí)調(diào)用 react-reconcile 模塊的 createContainer 傳入 getElementById(root) 執(zhí)行 createFiberRoot 生成一個(gè) FiberRoot 對(duì)象掛載到實(shí)例的 _internalRoot

export function createFiberRoot(
  containerInfo: any,
  isConcurrent: boolean,
  hydrate: boolean,
): FiberRoot { 
  const uninitializedFiber = createHostRootFiber(isConcurrent);
  return  root = ({
      current: uninitializedFiber, // Fiber 對(duì)象通過(guò) new Fiber 創(chuàng)建,與 ReactElement 對(duì)應(yīng)也是樹(shù)狀結(jié)構(gòu),這里樹(shù)的頂點(diǎn)
      containerInfo: containerInfo, // 通過(guò) render 方法傳入的 root 節(jié)點(diǎn), 應(yīng)用掛載的節(jié)點(diǎn)
      pendingChildren: null, // ssr 用來(lái)復(fù)用節(jié)點(diǎn)的
      // 任務(wù)調(diào)度的時(shí)間標(biāo)記優(yōu)先級(jí)
      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,
      // 標(biāo)記渲染過(guò)程中是否有錯(cuò)誤
      didError: false,
      // 正在等待提交的任務(wù)
      pendingCommitExpirationTime: NoWork,
      finishedWork: null, // 記錄一次更新渲染過(guò)程完成的任務(wù) Fiber 對(duì)象
      timeoutHandle: noTimeout, 
      context: null,
      pendingContext: null,
      hydrate,
      nextExpirationTimeToWorkOn: NoWork, // 標(biāo)記此次更新要執(zhí)行的是哪個(gè)優(yōu)先級(jí)的任務(wù)
      expirationTime: NoWork, // 用在調(diào)度中
      firstBatch: null,
      nextScheduledRoot: null, // 鏈表屬性

      interactionThreadID: unstable_getThreadID(),
      memoizedInteractions: new Set(),
      pendingInteractionMap: new Map(),
    }: FiberRoot);
}

Fiber

  • FiberRoot.current 就是一個(gè) Fiber 對(duì)象
  • 每一個(gè) ReactElement 對(duì)應(yīng)一個(gè) Fiber 對(duì)象
  • 記錄節(jié)點(diǎn)的各種狀態(tài)
    class 組件的 this.state、this.props ,在 Fiber 更新后才會(huì)更新 class 組件上的 this.state, props,也是 hooks 實(shí)現(xiàn)的原理,function 組件是沒(méi)有 this.state this.props 的,F(xiàn)iber 有能力記錄這些狀態(tài)之后在 function 組件更新后拿到這些狀態(tài)。
  • 串聯(lián)整個(gè)應(yīng)用形成的樹(shù)結(jié)構(gòu)

ReactElement 對(duì)應(yīng)的結(jié)構(gòu)

Fiber 的數(shù)據(jù)結(jié)構(gòu)

FiberNode 有三個(gè)屬性 return、child、sibling, 分別代表此 Fiber 對(duì)象的父節(jié)點(diǎn),第一個(gè)子節(jié)點(diǎn),自己的兄弟節(jié)點(diǎn)

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag; // 標(biāo)記不同的組件類(lèi)型,不同的更新方式
  this.key = key;  // key
  this.elementType = null; // createElement 第一個(gè)參數(shù),組件 或者 標(biāo)簽
  this.type = null; //  記錄異步組件 resolved 后是 class 還是 function 組件
  this.stateNode = null; // 節(jié)點(diǎn)的實(shí)例,對(duì)應(yīng) class 組件或者 dom 節(jié)點(diǎn),function 沒(méi)有實(shí)例就沒(méi) stateNode

  // Fiber
  this.return = null; // 父親節(jié)點(diǎn)
  this.child = null; // 第一個(gè)子節(jié)點(diǎn)
  this.sibling = null; // 自己的下一個(gè)兄弟節(jié)點(diǎn)
  this.index = 0;

  this.ref = null; // ref

  this.pendingProps = pendingProps; // 每個(gè)創(chuàng)建的新 props
  this.memoizedProps = null; // 老 props
  this.updateQueue = null; // 節(jié)點(diǎn)創(chuàng)建的 update 對(duì)象 queue 
  this.memoizedState = null; // 老 state,新 state 是由 updateQueue 計(jì)算出來(lái)的然后覆蓋這里
  this.firstContextDependency = null;  // context 相關(guān)

  this.mode = mode; // 標(biāo)記時(shí)創(chuàng)建,繼承父節(jié)點(diǎn) mod

  // Effects 副作用 
  this.effectTag = NoEffect; 
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork; // 任務(wù)的過(guò)期時(shí)間
  this.childExpirationTime = NoWork; // 子節(jié)點(diǎn)更新的過(guò)期時(shí)間

  this.alternate = null; // Fiber 用來(lái)復(fù)制復(fù)用 Fiber 的。

  if (enableProfilerTimer) {
    this.actualDuration = 0;
    this.actualStartTime = -1;
    this.selfBaseDuration = 0;
    this.treeBaseDuration = 0;
  }
}

Fiber 對(duì)象對(duì)應(yīng)的結(jié)構(gòu)

update 和 updateQueue

  • 用于記錄組件狀態(tài)的改變
    記錄改變的方式和內(nèi)容
  • 存放在 updateQueue
    存放多個(gè) update 用來(lái)計(jì)算出最終改變的結(jié)果
  • 多個(gè) update 可以同時(shí)存在
    setState 三次會(huì)創(chuàng)建三個(gè)update,放到 updateQueue 里

update 結(jié)構(gòu)

在 legacyRenderSubtreeIntoContainer 最終調(diào)用 ReactRoot.prototype.render 時(shí)執(zhí)行 scheduleRootUpdate 里執(zhí)行 createUpdate

export function createUpdate(expirationTime: ExpirationTime): Update<*> {
  return {
    expirationTime: expirationTime, // 當(dāng)前更新的過(guò)期時(shí)間

    tag: UpdateState, // 四個(gè)狀態(tài),更新updateState 0、替換replaceState 1、強(qiáng)制forceUpdate 2、throw 捕獲 captureUpdate 3
    payload: null, // 實(shí)際操作內(nèi)容,在外面賦值上去 update.payload = { element } 初次渲染傳入的是元素,setState 可能傳入的就是對(duì)象或者方法
    callback: null,

    next: null, // 下一個(gè) update 單向鏈表
    nextEffect: null,
  };
}

updateQueue 結(jié)構(gòu)

export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    baseState, // 每次操作更新之后計(jì)算出的 state,作為下一次計(jì)算的基礎(chǔ)
    firstUpdate: null,  // 記錄鏈表結(jié)構(gòu)
    lastUpdate: null, // 記錄鏈表結(jié)構(gòu)
    firstCapturedUpdate: null, // 記錄鏈表結(jié)構(gòu)
    lastCapturedUpdate: null, // 記錄鏈表結(jié)構(gòu)
    firstEffect: null,
    lastEffect: null,
    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}

enqueueUpdated 方法

enqueueUpdated 就是在 fiber 對(duì)象上創(chuàng)建一個(gè) updateQueue,然后把 update 對(duì)象傳入到這個(gè) queue 里

export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // ...
  let queue1
  queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
  appendUpdateToQueue(queue1, update);
}
function appendUpdateToQueue<State>(
  queue: UpdateQueue<State>,
  update: Update<State>,
) {
  // Append the update to the end of the list.
  if (queue.lastUpdate === null) {
    // Queue is empty
    queue.firstUpdate = queue.lastUpdate = update;
  } else {
    queue.lastUpdate.next = update;
    queue.lastUpdate = update;
  }
}

expirationTime

初次渲染最后執(zhí)行 ReactDOM.render 調(diào)用 ReactRoot.prototype.render 中調(diào)用 react-reconcile 模塊的 updateContainer 會(huì)計(jì)算一個(gè) expirationTime 然后用這個(gè)時(shí)間創(chuàng)建 update 對(duì)象推入 updateQueue 內(nèi)

expirationTime 的計(jì)算

export function updateContainer(
  element: ReactNodeList, // App
  container: OpaqueRoot, // 一個(gè) fiber root
  parentComponent: ?React$Component<any, any>, // null
  callback: ?Function,
): ExpirationTime {
  const current = container.current; // Fiber
  const currentTime = requestCurrentTime(); // 創(chuàng)建一個(gè)時(shí)間差
  // 計(jì)算出一個(gè)時(shí)間,ConcurrentMode 會(huì)用到, 計(jì)算出的是優(yōu)先級(jí)時(shí)間
  const expirationTime = computeExpirationForFiber(currentTime, current);
  // 主要執(zhí)行
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}
  • requestCurrentTime 方法返回一個(gè)固定的常量,調(diào)用 recomputeCurrentRendererTime 返回當(dāng)前渲染時(shí)間直到 js 加載初的時(shí)間差值,差值小的值會(huì)在 msToExpirationTime 被計(jì)算成同一個(gè)常數(shù)
function requestCurrentTime() {
  // ...
  if ( // 沒(méi)有調(diào)度時(shí)間,初次渲染
    nextFlushedExpirationTime === NoWork ||
    nextFlushedExpirationTime === Never
  ) {
    // If there's no pending work, or if the pending work is offscreen, we can
    // read the current time without risk of tearing.
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;
    return currentSchedulerTime;
  }
  // ...
}
function recomputeCurrentRendererTime() {
  const currentTimeMs = now() - originalStartTimeMs; // js 加載到現(xiàn)在渲染的固定值
  currentRendererTime = msToExpirationTime(currentTimeMs);
}
export function msToExpirationTime(ms: number): ExpirationTime {
  // Always add an offset so that we don't clash with the magic number for NoWork.
  return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET;
}
  • computeExpirationForFiber 方法會(huì)根據(jù)當(dāng)前渲染的 currentTime 計(jì)算出一個(gè)優(yōu)先級(jí)時(shí)間。

function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {
  let expirationTime;
  // ...
  if (fiber.mode & ConcurrentMode) { // 異步 mode 才計(jì)算 expirationTime
    if (isBatchingInteractiveUpdates) {
      // This is an interactive update
      expirationTime = computeInteractiveExpiration(currentTime);
    } else {
      // This is an async update
      expirationTime = computeAsyncExpiration(currentTime);
   }
 }
 // ...
 return expirationTime;
}
  • computeExpirationForFiber 的核心就是根據(jù)渲染方式的 mod 不同來(lái)創(chuàng)建不同優(yōu)先級(jí)的 expirationTime,區(qū)別就在于傳入 computeExpirationBucket 的參數(shù)不同
  • 最終的公式 ((((currentTime - 2 + 5000 / 10) / 25) | 0) + 1) * 25, 公式用了 | 0取整,使得在 * 25 倍數(shù)之間很短的一段時(shí)間粒度里計(jì)算出的值是一樣的
  • 在很短時(shí)間內(nèi) setState 多次調(diào)用更新時(shí),也可以保證是同一優(yōu)先級(jí)的更新。
export function computeAsyncExpiration(
  currentTime: ExpirationTime,
): ExpirationTime {
  return computeExpirationBucket(
    currentTime,
    LOW_PRIORITY_EXPIRATION,
    LOW_PRIORITY_BATCH_SIZE,
  );
}
export function computeInteractiveExpiration(currentTime: ExpirationTime) {
  return computeExpirationBucket(
    currentTime,
    HIGH_PRIORITY_EXPIRATION,
    HIGH_PRIORITY_BATCH_SIZE,
  );
}

不同的 expirationTime

Sync 模式 優(yōu)先級(jí)高
Async 模式 會(huì)進(jìn)行調(diào)度可能會(huì)被中斷,會(huì)計(jì)算出 expirationTime 來(lái)分配優(yōu)先級(jí)
指定 context

同步更新的 expirationTime

  • computeExpirationForFiber 在異步 mode 的情況下才根據(jù) currentTime 來(lái)計(jì)算 expirationTime
  • expirationTime 等于 Sync 1, NoWork 0,還有就是計(jì)算出來(lái)的時(shí)間值
  • 在 expirationContext 不為 NoWork 時(shí),expirationContext 會(huì)根據(jù)更新 api 方式的不同設(shè)置為 Sync 或者 計(jì)算出 Async 方式的優(yōu)先級(jí)時(shí)間
function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {
  let expirationTime;
  if (expirationContext !== NoWork) {
    // An explicit expiration context was set;
    expirationTime = expirationContext;
  } else if (isWorking) {
    if (isCommitting) {
      // Updates that occur during the commit phase should have sync priority
      // by default.
      expirationTime = Sync;
    } else {
      // Updates during the render phase should expire at the same time as
      // the work that is being rendered.
      expirationTime = nextRenderExpirationTime;
    }
  } else {
    // No explicit expiration context was set, and we're not currently
    // performing work. Calculate a new expiration time.
    if (fiber.mode & ConcurrentMode) {
    // 異步邏輯
    } else {
      // This is a sync update
      expirationTime = Sync;
    }
  }
  return expirationTime;
}

setState 和 forceUpdate

  • 給節(jié)點(diǎn)的 Fiber 創(chuàng)建更新
  • 更新的類(lèi)型不同

來(lái)源

都是 Component 組件上的原型方法, 他們調(diào)用的 enqueueSetState 等方法都是由不同平臺(tái)創(chuàng)建的 updater 對(duì)象上的,瀏覽器中的 updater 對(duì)象來(lái)自 ReactFiberClassComponent.js 里的 classComponentUpdater 對(duì)象上

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // 作為參數(shù)讓不同的平臺(tái)來(lái)控制屬性更新的方式
  this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.setState = function(partialState, callback) {
  // 僅僅調(diào)用了 updater 上的方法 updater 是初始化的第三個(gè)參數(shù)的實(shí)例屬性,跟平臺(tái)相關(guān)
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

classComponentUpdater

  • enqueueSetState, enqueueForceUpdate 幾乎是相同的,在這里我們只看 enqueueSetState
    1 從 Map 對(duì)象中獲取 Fiber 對(duì)象
    2 創(chuàng)建當(dāng)前時(shí)間
    3 優(yōu)先級(jí)時(shí)間
    4 創(chuàng)建 update
    5 設(shè)置payload
    6 執(zhí)行回調(diào)
    7 把 update 對(duì)象推入 Fiber 對(duì)象的 updateQueue 內(nèi)
    8 進(jìn)行調(diào)度
  • 發(fā)現(xiàn) enqueueSetState 所執(zhí)行的順序跟 ReactFiberReconclier.js 的 updateContainer 幾乎是一模一樣的
    1 Fiber 對(duì)象是參數(shù)傳進(jìn)來(lái)的
    2 payload 是創(chuàng)建 update 對(duì)象后在外面賦值的
    3 最后也是創(chuàng)建 update 進(jìn)入 Fiber 對(duì)象的 updateQueue 再進(jìn)行調(diào)度
const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    const fiber = ReactInstanceMap.get(inst); // 從 Map 對(duì)象中獲取 Fiber 對(duì)象
    const currentTime = requestCurrentTime(); // 創(chuàng)建當(dāng)前時(shí)間
    const expirationTime = computeExpirationForFiber(currentTime, fiber); // 優(yōu)先級(jí)時(shí)間

    const update = createUpdate(expirationTime); // 創(chuàng)建 update
    update.payload = payload; // 設(shè)置payload
    if (callback !== undefined && callback !== null) {
      update.callback = callback; // 執(zhí)行回調(diào)
    }

    enqueueUpdate(fiber, update); // 把 update 對(duì)象推入 Fiber 對(duì)象的 updateQueue 內(nèi)
    scheduleWork(fiber, expirationTime); // 進(jìn)行調(diào)度
  },
  enqueueReplaceState(inst, payload, callback) {
    // ...
  },
  enqueueForceUpdate(inst, callback) {
    // ...
  }
}; 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 上次寫(xiě)了react整體框架的理解,這次想寫(xiě)寫(xiě)看對(duì)于新版React的新的React Fiber的實(shí)現(xiàn)。 在React...
    離開(kāi)North閱讀 1,709評(píng)論 1 2
  • 40、React 什么是React?React 是一個(gè)用于構(gòu)建用戶(hù)界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,179評(píng)論 0 1
  • 前言 Facebook 的研發(fā)能力真是驚人, Fiber 架構(gòu)給 React 帶來(lái)了新視野的同時(shí),將調(diào)度一詞介紹給...
    Floveluy閱讀 1,880評(píng)論 1 5
  • 在《JavaScript異步機(jī)制》這篇文章中我們說(shuō)到,Js引擎是單線(xiàn)程的,它負(fù)責(zé)維護(hù)任務(wù)棧,并通過(guò) Event L...
    tobAlier閱讀 3,100評(píng)論 3 6
  • 這都是我以前畫(huà)的,喜歡的就點(diǎn)贊(?ò ? ó?)
    傾城戀雨閱讀 485評(píng)論 11 0

友情鏈接更多精彩內(nèi)容