React 源碼解析 React 的更新

React 的更新

只解析到創(chuàng)建更新進入調度器

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

  • 初始渲染
    ReactDOM.render
    ReactDOM.hydrate

  • 更新渲染
    setState
    forceUpdate (舍棄)

ReactDOM.render

步驟

  • 創(chuàng)建 ReactRoot
    最頂點的對象
  • 創(chuàng)建 FiberRoot 和 RootFiber
  • 創(chuàng)建更新 update
    用來更新調度, 進入調度后 setState 或 ReactDOM.render 都的調度器去管理的

初始渲染

ReactDOM.render

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

初始渲染

在 react-dom/client/ReactDOM.js 文件下, 有 render hydrate 兩個方法典徊, render 是用在瀏覽器環(huán)境內的亿眠,hydrate 用在服務器環(huán)境下帆焕,唯一的區(qū)別是調用 legacyRenderSubtreeIntoContainer 方法傳入的第四個參數不同喳魏。

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

  if (callback !== null) {
    work.then(callback);
  }
  // render 的重點調用
  DOMRenderer.updateContainer(children, root, null, work._onCommit);
  return work;
};
  • DOMRenderer.updateContainer 中計算出一個 expirationTime 傳入了 updateContainerAtExpirationTime.
export function updateContainer(
  element: ReactNodeList, // App
  container: OpaqueRoot, // 一個 fiber root
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  const currentTime = requestCurrentTime(); // 創(chuàng)建一個時間差
  // 計算出一個時間永毅,ConcurrentMode 會用到
  const expirationTime = computeExpirationForFiber(currentTime, current);
  // 主要執(zhí)行
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}
  • updateContainerAtExpirationTime 中調用 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 來標記 react 需要更新的點
  • 設置完 update 屬性再調用 enqueueUpdate 把 update 放入更新隊列里
    react 更新會在一個節(jié)點上整體進行很多個更新,這個更新 queue 就是管理多次更新的作用
  • 最后執(zhí)行 scheduleWork 通知 react 進行調度人弓,根據任務的優(yōu)先級進行更新沼死。
function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {

  const update = createUpdate(expirationTime); // 用來標記 react 更新的節(jié)點
  // 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;
  }
  // 把更新的對象加入到 fiber 對象上的 updateQueue 里, 會有很多更新在一個節(jié)點上產生
  enqueueUpdate(current, update);
  // 開始進行調度
  scheduleWork(current, expirationTime);
  return expirationTime;
}

ReactDOM.render 階段總結

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

FiberRoot

  • 整個應用的起點
  • 包含了傳入的 getElementById(root)
  • 記錄整個應用更新過程的各種信息 containerInfo (包含了 root 節(jié)點等信息)

FiberRoot 對象結構

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

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

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

Fiber

  • FiberRoot.current 就是一個 Fiber 對象
  • 每一個 ReactElement 對應一個 Fiber 對象
  • 記錄節(jié)點的各種狀態(tài)
    class 組件的 this.state吩翻、this.props 兜看,在 Fiber 更新后才會更新 class 組件上的 this.state, props,也是 hooks 實現的原理狭瞎,function 組件是沒有 this.state this.props 的细移,Fiber 有能力記錄這些狀態(tài)之后在 function 組件更新后拿到這些狀態(tài)。
  • 串聯整個應用形成的樹結構

ReactElement 對應的結構

Fiber 的數據結構

FiberNode 有三個屬性 return熊锭、child弧轧、sibling, 分別代表此 Fiber 對象的父節(jié)點,第一個子節(jié)點碗殷,自己的兄弟節(jié)點

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag; // 標記不同的組件類型精绎,不同的更新方式
  this.key = key;  // key
  this.elementType = null; // createElement 第一個參數,組件 或者 標簽
  this.type = null; //  記錄異步組件 resolved 后是 class 還是 function 組件
  this.stateNode = null; // 節(jié)點的實例锌妻,對應 class 組件或者 dom 節(jié)點代乃,function 沒有實例就沒 stateNode

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

  this.ref = null; // ref

  this.pendingProps = pendingProps; // 每個創(chuàng)建的新 props
  this.memoizedProps = null; // 老 props
  this.updateQueue = null; // 節(jié)點創(chuàng)建的 update 對象 queue 
  this.memoizedState = null; // 老 state,新 state 是由 updateQueue 計算出來的然后覆蓋這里
  this.firstContextDependency = null;  // context 相關

  this.mode = mode; // 標記時創(chuàng)建仿粹,繼承父節(jié)點 mod

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

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

  this.expirationTime = NoWork; // 任務的過期時間
  this.childExpirationTime = NoWork; // 子節(jié)點更新的過期時間

  this.alternate = null; // Fiber 用來復制復用 Fiber 的搁吓。

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

Fiber 對象對應的結構

update 和 updateQueue

  • 用于記錄組件狀態(tài)的改變
    記錄改變的方式和內容
  • 存放在 updateQueue
    存放多個 update 用來計算出最終改變的結果
  • 多個 update 可以同時存在
    setState 三次會創(chuàng)建三個update原茅,放到 updateQueue 里

update 結構

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

export function createUpdate(expirationTime: ExpirationTime): Update<*> {
  return {
    expirationTime: expirationTime, // 當前更新的過期時間

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

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

updateQueue 結構

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

enqueueUpdated 方法

enqueueUpdated 就是在 fiber 對象上創(chuàng)建一個 updateQueue仿吞,然后把 update 對象傳入到這個 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 調用 ReactRoot.prototype.render 中調用 react-reconcile 模塊的 updateContainer 會計算一個 expirationTime 然后用這個時間創(chuàng)建 update 對象推入 updateQueue 內

expirationTime 的計算

export function updateContainer(
  element: ReactNodeList, // App
  container: OpaqueRoot, // 一個 fiber root
  parentComponent: ?React$Component<any, any>, // null
  callback: ?Function,
): ExpirationTime {
  const current = container.current; // Fiber
  const currentTime = requestCurrentTime(); // 創(chuàng)建一個時間差
  // 計算出一個時間滑频,ConcurrentMode 會用到, 計算出的是優(yōu)先級時間
  const expirationTime = computeExpirationForFiber(currentTime, current);
  // 主要執(zhí)行
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}
  • requestCurrentTime 方法返回一個固定的常量,調用 recomputeCurrentRendererTime 返回當前渲染時間直到 js 加載初的時間差值唤冈,差值小的值會在 msToExpirationTime 被計算成同一個常數
function requestCurrentTime() {
  // ...
  if ( // 沒有調度時間,初次渲染
    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 加載到現在渲染的固定值
  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 方法會根據當前渲染的 currentTime 計算出一個優(yōu)先級時間塞蹭。

function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {
  let expirationTime;
  // ...
  if (fiber.mode & ConcurrentMode) { // 異步 mode 才計算 expirationTime
    if (isBatchingInteractiveUpdates) {
      // This is an interactive update
      expirationTime = computeInteractiveExpiration(currentTime);
    } else {
      // This is an async update
      expirationTime = computeAsyncExpiration(currentTime);
   }
 }
 // ...
 return expirationTime;
}
  • computeExpirationForFiber 的核心就是根據渲染方式的 mod 不同來創(chuàng)建不同優(yōu)先級的 expirationTime蓝纲,區(qū)別就在于傳入 computeExpirationBucket 的參數不同
  • 最終的公式 ((((currentTime - 2 + 5000 / 10) / 25) | 0) + 1) * 25, 公式用了 | 0取整暗赶,使得在 * 25 倍數之間很短的一段時間粒度里計算出的值是一樣的
  • 在很短時間內 setState 多次調用更新時,也可以保證是同一優(yōu)先級的更新傅物。
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)先級高
Async 模式 會進行調度可能會被中斷,會計算出 expirationTime 來分配優(yōu)先級
指定 context

同步更新的 expirationTime

  • computeExpirationForFiber 在異步 mode 的情況下才根據 currentTime 來計算 expirationTime
  • expirationTime 等于 Sync 1琉预, NoWork 0董饰,還有就是計算出來的時間值
  • 在 expirationContext 不為 NoWork 時,expirationContext 會根據更新 api 方式的不同設置為 Sync 或者 計算出 Async 方式的優(yōu)先級時間
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é)點的 Fiber 創(chuàng)建更新
  • 更新的類型不同

來源

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

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // 作為參數讓不同的平臺來控制屬性更新的方式
  this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.setState = function(partialState, callback) {
  // 僅僅調用了 updater 上的方法 updater 是初始化的第三個參數的實例屬性卒暂,跟平臺相關
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

classComponentUpdater

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

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

    enqueueUpdate(fiber, update); // 把 update 對象推入 Fiber 對象的 updateQueue 內
    scheduleWork(fiber, expirationTime); // 進行調度
  },
  enqueueReplaceState(inst, payload, callback) {
    // ...
  },
  enqueueForceUpdate(inst, callback) {
    // ...
  }
}; 
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末娄帖,一起剝皮案震驚了整個濱河市也祠,隨后出現的幾起案子,更是在濱河造成了極大的恐慌近速,老刑警劉巖诈嘿,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異削葱,居然都是意外死亡奖亚,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門析砸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昔字,“玉大人,你說我怎么就攤上這事干厚±畹危” “怎么了螃宙?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長所坯。 經常有香客問我谆扎,道長,這世上最難降的妖魔是什么芹助? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任堂湖,我火速辦了婚禮,結果婚禮上状土,老公的妹妹穿的比我還像新娘无蜂。我一直安慰自己,他們只是感情好蒙谓,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布斥季。 她就那樣靜靜地躺著,像睡著了一般累驮。 火紅的嫁衣襯著肌膚如雪酣倾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天谤专,我揣著相機與錄音躁锡,去河邊找鬼。 笑死置侍,一個胖子當著我的面吹牛映之,可吹牛的內容都是我干的。 我是一名探鬼主播蜡坊,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼杠输,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了算色?” 一聲冷哼從身側響起抬伺,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灾梦,沒想到半個月后峡钓,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡若河,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年能岩,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萧福。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拉鹃,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情膏燕,我是刑警寧澤钥屈,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站坝辫,受9級特大地震影響篷就,放射性物質發(fā)生泄漏。R本人自食惡果不足惜近忙,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一竭业、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧及舍,春花似錦未辆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至攘残,卻和暖如春炕桨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肯腕。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钥平,地道東北人实撒。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像涉瘾,于是被迫代替她去往敵國和親知态。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容

  • 上次寫了react整體框架的理解立叛,這次想寫寫看對于新版React的新的React Fiber的實現负敏。 在React...
    離開North閱讀 1,591評論 1 2
  • 40、React 什么是React秘蛇?React 是一個用于構建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,014評論 0 1
  • 前言 Facebook 的研發(fā)能力真是驚人其做, Fiber 架構給 React 帶來了新視野的同時,將調度一詞介紹給...
    Floveluy閱讀 1,768評論 1 5
  • 在《JavaScript異步機制》這篇文章中我們說到赁还,Js引擎是單線程的妖泄,它負責維護任務棧,并通過 Event L...
    tobAlier閱讀 2,985評論 3 6
  • 這都是我以前畫的艘策,喜歡的就點贊(?ò ? ó?)
    傾城戀雨閱讀 437評論 11 0