react解析: render的中的update(四)

感謝 yck: 剖析 React 源碼解析,本篇文章是在讀完他的文章的基礎上,將他的文章進行拆解和加工,加入我自己的一下理解和例子,便於大家理解。覺得yck寫的真的很棒 。React 版本爲 16.8.6,關於源碼的閱讀,可以移步到yck react源碼解析

本文永久有效鏈接: react解析: render的中的update(四)

上一章節說到,不存在root數據節點,即通過createFiberRoot 函數創建FiberRootFiberRoot對象是整個React應用的起點,同時也記錄了整個React應用更新過程中的各種信息。

下面將要聊到的就是,當root唄創建後,還會發生什麼👇👇

legacyRenderSubtreeIntoContainer 函數

下面銜接上一部分內容,不懂得可以查看上一章節

yck: ReactDOM 源碼 554行 legacyRenderSubtreeIntoContainer
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
    // 初始化時,container 肯定沒有 _reactRootContainer屬性
    let root: Root = (container._reactRootContainer: any);
    if (!root) {
        // 省略創建root部分
        
        unbatchedUpdates(() => {
            if (parentComponent != null) {
                root.legacy_renderSubtreeIntoContainer(
                    parentComponent,
                    children,
                    callback,
                );
            } else {
                root.render(children, callback);
            }
        });
    }
}

在root剛剛被創建時,parentComponent一般都爲null;

unbatchedUpdates函數在這裏作用是:告知React內部不進行批量更新,即不用將多個setState合併爲一個;
setState在後面的章節我們將會說到

那麼這裏實際調用的就是root.render函數,root是ReactRoot實例對象,即調用 root.render函數 == ReactRoot.prototype.render函數

ReactRoot.prototype.render 函數

yck: ReactRoot 源碼 377行 ReactRoot.prototype.render
ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  // 這裏指 FiberRoot
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;

  // 如果有 callback,就 push 進 work 中的數組
  if (callback !== null) {
    work.then(callback);
  }
  // work._onCommit 就是用於執行所有回調函數的
  updateContainer(children, root, null, work._onCommit);
  return work;
};

函數中的參數children即是ReactElement節點對象,callback爲回調函數。ReactWork實例對象的主要作用就是維護一個回調數組,可查看yck: ReactWork 源碼 327行,如果傳入參數中存在callback,就將其掛載ReactWork實例對象中;

下面來看看updateContainer函數會做什麼。

updateContainer 函數

yck: ReactFiberReconciler 源碼 284行 updateContainer
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  // 計算時間
  const currentTime = requestCurrentTime();
  // expirationTime 代表優先級,數字越大優先級越高
  // sync 的數字是最大的,所以優先級也是最高的
  const expirationTime = computeExpirationForFiber(currentTime, current);
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}

container.current即是從FiberRoot中取出RootFiber對象,currentTime就是當前距離React應用初始化的時間。 **expirationTime字面意思就是過期時間,後面我會專門花一章的時間來介紹這兩個時間,這兩個時間也是React應用任務調度的重點。

scheduleRootUpdate函數

updateContainerAtExpirationTime函數實際調用的就是scheduleRootUpdate函數,下面來說一下scheduleRootUpdate函數的作用。

yck: ReactFiberReconciler 源碼 114行 scheduleRootUpdate
function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {
  // 創建一個 update,就是內部有幾個屬性的對象
  const update = createUpdate(expirationTime);
  update.payload = {element};

  // render中的回調函數 
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    update.callback = callback;
  }

  flushPassiveEffects();
  // 把 update 入隊,內部就是一些創建或者獲取 queue(鏈表結構),然後給鏈表添加一個節點的操作
  enqueueUpdate(current, update);
  scheduleWork(current, expirationTime);

  return expirationTime;
}

下面就是update對象其中的屬性:

// update對象屬性
export type Update<State> = {
  // 更新的過期時間
  expirationTime: ExpirationTime,

  // export const UpdateState = 0;
  // export const ReplaceState = 1;
  // export const ForceUpdate = 2;
  // export const CaptureUpdate = 3;
  // 指定更新的類型,值爲以上幾種
  tag: 0 | 1 | 2 | 3,
  // 更新內容,比如`setState`接收的第一個參數
  payload: any,
  // 對應的回調,`setState`,`render`都有
  callback: (() => mixed) | null,

  // 指向下一個更新
  next: Update<State> | null,
  // 指向下一個`side effect`
  nextEffect: Update<State> | null,
};

udate對象會被插入到React應用維護的任務隊列中,不管你是setState還是ReactDOM.render造成的 React應用 更新都是如此。這個函數核心作用就是創建或者獲取一個隊列,然後把 update 對象插入隊列進行更新。scheduleWork函數就是任務調度的東西了。

更多內容:

react解析: React.createElement(一)

react解析: React.Children(二)

react解析: render的FiberRoot(三)

參考:

yck: 剖剖析 React 源碼

Jokcy 的 《React 源碼解析》: react.jokcy.me/

ps: 順便推一下自己的個人公衆號:Yopai,有興趣的可以關注,每週不定期更新,分享可以增加世界的快樂

image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章