react16 版本源碼 簡單分析(2)

首先 我這裏 有一份配置好的 react源碼配置 地址以下node

倉庫 地址react

git地址:git@github.com:544076724/react16-source.gitgit

下載下來直接 yarn start啓動就能夠了
前言:react項目是基於Monorepo 結構開發的,後續文章中所出現的地址都是咱們工程的
src/react/packages/ 這一級來找的具體包github

接下來咱們簡單作下分析算法

咱們這篇裏面暫時不涉及太多的優先級任務,react源碼裏的優先級任務 是經過計算一個過時時間來計算的 他們本身實現的一套算法 這裏暫時不介紹這個算法 你們知道這個優先級算法是requestIdleCallback的完善版 就能夠segmentfault

咱們知道前面說了fiber結構 而fiber的結構是由vnode 轉換過來的,JSX 被 Babel 編譯爲 React.createElement 方法的調用,createElement 方法在調用後返回的就是 ReactElement,就是 virtualDOM。數組

createElement

文件位置:packages/react/src/ReactElement.js服務器

/**
 * 建立 React Element
 * type      元素類型
 * config    配置屬性
 * children  子元素
 * 1. 分離 props 屬性和特殊屬性
 * 2. 將子元素掛載到 props.children 中
 * 3. 爲 props 屬性賦默認值 (defaultProps)
 * 4. 建立並返回 ReactElement
 */
export function createElement(type, config, children) {
  /**
   * propName -> 屬性名稱
   * 用於後面的 for 循環
   */
  let propName;

  /**
   * 存儲 React Element 中的普通元素屬性 即不包含 key ref self source
   */
  const props = {};

  /**
   * 待提取屬性
   * React 內部爲了實現某些功能而存在的屬性
   */
  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  // 若是 config 不爲 null
  if (config != null) {
    // 若是 config 對象中有合法的 ref 屬性
    if (hasValidRef(config)) {
      // 將 config.ref 屬性提取到 ref 變量中
      ref = config.ref;
      // 在開發環境中
      if (__DEV__) {
        // 若是 ref 屬性的值被設置成了字符串形式就報一個提示
        // 說明此用法在未來的版本中會被刪除
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }
    // 若是在 config 對象中擁有合法的 key 屬性
    if (hasValidKey(config)) {
      // 將 config.key 屬性中的值提取到 key 變量中
      key = '' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // 遍歷 config 對象
    for (propName in config) {
      // 若是當前遍歷到的屬性是對象自身屬性
      // 而且在 RESERVED_PROPS 對象中不存在該屬性
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        // 將知足條件的屬性添加到 props 對象中 (普通屬性)
        props[propName] = config[propName];
      }
    }
  }

  /**
   * 將第三個及以後的參數掛載到 props.children 屬性中
   * 若是子元素是多個 props.children 是數組
   * 若是子元素是一個 props.children 是對象
   */

  // 因爲從第三個參數開始及之後都表示子元素
  // 因此減去前兩個參數的結果就是子元素的數量
  const childrenLength = arguments.length - 2;
  // 若是子元素的數量是 1
  if (childrenLength === 1) {
    // 直接將子元素掛載到到 props.children 屬性上
    // 此時 children 是對象類型
    props.children = children;
    // 若是子元素的數量大於 1
  } else if (childrenLength > 1) {
    // 建立數組, 數組中元素的數量等於子元素的數量
    const childArray = Array(childrenLength);
    // 開啓循環 循環次匹配子元素的數量
    for (let i = 0; i < childrenLength; i++) {
      // 將子元素添加到 childArray 數組中
      // i + 2 的緣由是實參集合的前兩個參數不是子元素
      childArray[i] = arguments[i + 2];
    }
    // 若是是開發環境
    if (__DEV__) {
      // 若是 Object 對象中存在 freeze 方法
      if (Object.freeze) {
        // 調用 freeze 方法 凍結 childArray 數組
        // 防止 React 核心對象被修改 凍結對象提升性能
        Object.freeze(childArray);
      }
    }
    // 將子元素數組掛載到 props.children 屬性中
    props.children = childArray;
  }

  /**
   * 若是當前處理是組件
   * 看組件身上是否有 defaultProps 屬性
   * 這個屬性中存儲的是 props 對象中屬性的默認值
   * 遍歷 defaultProps 對象 查看對應的 props 屬性的值是否爲 undefined
   * 若是爲undefined 就將默認值賦值給對應的 props 屬性值
   */

  // 將 type 屬性值視爲函數 查看其中是否具備 defaultProps 屬性
  if (type && type.defaultProps) {
    // 將 type 函數下的 defaultProps 屬性賦值給 defaultProps 變量
    const defaultProps = type.defaultProps;
    // 遍歷 defaultProps 對象中的屬性 將屬性名稱賦值給 propName 變量
    for (propName in defaultProps) {
      // 若是 props 對象中的該屬性的值爲 undefined
      if (props[propName] === undefined) {
        // 將 defaultProps 對象中的對應屬性的值賦值給 props 對象中的對應屬性的值
        props[propName] = defaultProps[propName];
      }
    }
  }

  /**
   * 在開發環境中 若是元素的 key 屬性 或者 ref 屬性存在
   * 監測開發者是否在組件內部經過 props 對象獲取了 key 屬性或者 ref 屬性
   * 若是獲取了 就報錯
   */

  // 若是處於開發環境
  if (__DEV__) {
    // 元素具備 key 屬性或者 ref 屬性
    if (key || ref) {
      // 看一下 type 屬性中存儲的是不是函數 若是是函數就表示當前元素是組件
      // 若是元素不是組件 就直接返回元素類型字符串
      // displayName 用於在報錯過程當中顯示是哪個組件報錯了
      // 若是開發者顯式定義了 displayName 屬性 就顯示開發者定義的
      // 否者就顯示組件名稱 若是組件也沒有名稱 就顯示 'Unknown'
      const displayName =
        typeof type === 'function'
          ? type.displayName || type.name || 'Unknown'
          : type;
      // 若是 key 屬性存在
      if (key) {
        // 爲 props 對象添加key 屬性
        // 並指定當經過 props 對象獲取 key 屬性時報錯
        defineKeyPropWarningGetter(props, displayName);
      }
      // 若是 ref 屬性存在
      if (ref) {
        // 爲 props 對象添加 ref 屬性
        // 並指定當經過 props 對象獲取 ref 屬性時報錯
        defineRefPropWarningGetter(props, displayName);
      }
    }
  }
  // 返回 ReactElement
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    // 在 Virtual DOM 中用於識別自定義組件
    ReactCurrentOwner.current,
    props,
  );
}

ReactElement

/**
 * 接收參數 返回 ReactElement
 */
const ReactElement = function (type, key, ref, self, source, owner, props) {
  const element = {
    /**
     * 組件的類型, 十六進制數值或者 Symbol 值
     * React 在最終在渲染 DOM 的時候, 須要確保元素的類型是 REACT_ELEMENT_TYPE
     * 須要此屬性做爲判斷的依據
     */
    $$typeof: REACT_ELEMENT_TYPE,

    /**
     * 元素具體的類型值 若是是元素節點 type 屬性中存儲的就是 div span 等等
     * 若是元素是組件 type 屬性中存儲的就是組件的構造函數
     */
    type: type,
    /**
     * 元素的惟一標識
     * 用做內部 vdom 比對 提高 DOM 操做性能
     */
    key: key,
    /**
     * 存儲元素 DOM 對象或者組件 實例對象
     */
    ref: ref,
    /**
     * 存儲向組件內部傳遞的數據
     */
    props: props,

    /**
     * 記錄當前元素所屬組件 (記錄當前元素是哪一個組件建立的)
     */
    _owner: owner,
  };
  // 返回 ReactElement
  return element;
};

該方法涉及到的 一些輔助函數 也都在這個文件中 你們能夠下載下來源代碼 而後看一下數據結構

虛擬dom 解析出來大概是這樣image.pngapp

能夠參考我以前寫的解析出來的結構 是相似的

而後就是咱們轉換出來的fiber節點的結構

type Fiber = {
  /************************  DOM 實例相關  *****************************/
  
  // 標記不一樣的組件類型, 值詳見 WorkTag
  tag: WorkTag,

  // 組件類型 div、span、組件構造函數
  type: any,

  // 實例對象, 如類組件的實例、原生 dom 實例, 而 function 組件沒有實例, 所以該屬性是空
  stateNode: any,
 
    /************************  構建 Fiber 樹相關  ***************************/
  
  // 指向本身的父級 Fiber 對象
  return: Fiber | null,

  // 指向本身的第一個子級 Fiber 對象
  child: Fiber | null,
  
  // 指向本身的下一個兄弟 iber 對象
  sibling: Fiber | null,
  
  // 在 Fiber 樹更新的過程當中,每一個 Fiber 都會有一個跟其對應的 Fiber
  // 咱們稱他爲 current <==> workInProgress
  // 在渲染完成以後他們會交換位置
  // alternate 指向當前 Fiber 在 workInProgress 樹中的對應 Fiber
    alternate: Fiber | null,
        
  /************************  狀態數據相關  ********************************/
  
  // 即將更新的 props
  pendingProps: any, 
  // 舊的 props
  memoizedProps: any,
  // 舊的 state
  memoizedState: any,
        
  /************************  反作用相關 ******************************/

  // 該 Fiber 對應的組件產生的狀態更新會存放在這個隊列裏面 組件的狀態
  updateQueue: UpdateQueue<any> | null,
  
  // 用來記錄當前 Fiber 要執行的 DOM 操做
  effectTag: SideEffectTag,

  // 存儲要執行的 DOM 操做
  firstEffect: Fiber | null,
  
  // 單鏈表用來快速查找下一個 side effect
  nextEffect: Fiber | null,
  
  // 存儲 DOM 操做完後的副租用 好比調用生命週期函數或者鉤子函數的調用
  lastEffect: Fiber | null,

  // 任務的過時時間 用於優先級 計算
  expirationTime: ExpirationTime,  
  
    // 當前組件及子組件處於何種渲染模式 詳見 TypeOfMode
  mode: TypeOfMode,
};

接來下 就該正式進入了,首先咱們react的app 通常都是以 ReactDOM.render掛載方法做爲入口

咱們先來說下首次渲染的狀況

接下來咱們找到render函數

render

文件位置:packages/react-dom/src/client/ReactDOMLegacy.js

/**
 * 渲染入口
 * element 要進行渲染的 ReactElement, createElement 方法的返回值
 * container 渲染容器 <div id="root"></div>
 * callback 渲染完成後執行的回調函數
 */
export function render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) {
  // 檢測 container 是不是符合要求的渲染容器
  // 即檢測 container 是不是真實的DOM對象
  // 若是不符合要求就報錯
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );
  return legacyRenderSubtreeIntoContainer(
    // 父組件 初始渲染沒有父組件 傳遞 null 佔位
    null,
    element,
    container,
    // 是否爲服務器端渲染 false 不是服務器端渲染 true 是服務器端渲染
    false,
    callback,
  );
}

由於咱們是串通主流程 因此直接看legacyRenderSubtreeIntoContainer方法

legacyRenderSubtreeIntoContainer

這個方法用來生成FiberRoot,這個裏面會建立Root 而後它下面_internalRoot會儲存fiberRoot。
文件位置: packages/react-dom/src/client/ReactDOMLegacy.js

/**
 * 將子樹渲染到容器中 (初始化 Fiber 數據結構: 建立 fiberRoot 及 rootFiber)
 * parentComponent: 父組件, 初始渲染傳入了 null
 * children: render 方法中的第一個參數, 要渲染的 ReactElement
 * container: 渲染容器
 * forceHydrate: true 爲服務端渲染, false 爲客戶端渲染
 * callback: 組件渲染完成後須要執行的回調函數
 **/
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: Container,
  forceHydrate: boolean,
  callback: ?Function,
) {
  /**
   * 檢測 container 是否已是初始化過的渲染容器
   * react 在初始渲染時會爲最外層容器添加 _reactRootContainer 屬性
   * react 會根據此屬性進行不一樣的渲染方式
   * root 不存在 表示初始渲染
   * root 存在 表示更新
   */
  // 獲取 container 容器對象下是否有 _reactRootContainer 屬性
  let root: RootType = (container._reactRootContainer: any);
  // 即將存儲根 Fiber 對象
  let fiberRoot;
  if (!root) {
    // 初始渲染
    // 初始化根 Fiber 數據結構
    // 爲 container 容器添加 _reactRootContainer 屬性
    // 在 _reactRootContainer 對象中有一個屬性叫作 _internalRoot
    // _internalRoot 屬性值即爲 FiberRoot 表示根節點 Fiber 數據結構
    // legacyCreateRootFromDOMContainer
    // createLegacyRoot
    // new ReactDOMBlockingRoot -> this._internalRoot
    // createRootImpl
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    // 獲取 Fiber Root 對象
    fiberRoot = root._internalRoot;
    /**
     * 改變 callback 函數中的 this 指向
     * 使其指向 render 方法第一個參數的真實 DOM 對象
     */
    // 若是 callback 參數是函數類型
    if (typeof callback === 'function') {
      // 使用 originalCallback 存儲 callback 函數
      const originalCallback = callback;
      // 爲 callback 參數從新賦值
      callback = function () {
        // 獲取 render 方法第一個參數的真實 DOM 對象
        // 實際上就是 id="root" 的 div 的子元素
        // rootFiber.child.stateNode
        // rootFiber 就是 id="root" 的 div
        const instance = getPublicRootInstance(fiberRoot);
        // 調用 callback 函數並改變函數內部 this 指向
        originalCallback.call(instance);
      };
    }
    // 初始化渲染不執行批量更新
    // 由於批量更新是異步的是能夠被打斷的, 可是初始化渲染應該儘快完成不能被打斷
    // 因此不執行批量更新
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    // 非初始化渲染 即更新
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function () {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  // 返回 render 方法第一個參數的真實 DOM 對象做爲 render 方法的返回值
  // 就是說渲染誰 返回誰的真實 DOM 對象
  return getPublicRootInstance(fiberRoot);
}

legacyCreateRootFromDOMContainer 該會方法會清空 容器 的元素(佔位元素) 並建立fiberRoot
看主流程 這個方法裏最終是調用的unbatchedUpdates 這個方法 初始執行的時候 會直接調用回調函數 也就是會直接調用 updateContainer 方法。

updateContainer

這個方法將任務(Update)存放於任務隊列(updateQueue)中 串並進行串聯
而且 調度和更新 current 對象 (render階段構建workInProgress樹 對比生成dom結構或篩選有更新的fiber節點 commit階段開始更新)
文件位置: packages/react-reconciler/src/ReactFiberReconciler.js

/**
 * 計算任務的過時時間
 * 再根據任務過時時間建立 Update 任務
 */
export function updateContainer(
    // element 要渲染的 ReactElement
  element: ReactNodeList,
  // container Fiber Root 對象
  container: OpaqueRoot,
  // parentComponent 父組件 初始渲染爲 null
  parentComponent: ?React$Component<any, any>,
  // ReactElement 渲染完成執行的回調函數
  callback: ?Function,
): ExpirationTime {  
  // container 獲取 rootFiber
  const current = container.current;
  // 獲取當前距離 react 應用初始化的時間 1073741805
  const currentTime = requestCurrentTimeForUpdate();
  // 異步加載設置
  const suspenseConfig = requestCurrentSuspenseConfig();

  // 計算過時時間
  // 爲防止任務由於優先級的緣由一直被打斷而未能執行
  // react 會設置一個過時時間, 當時間到了過時時間的時候
  // 若是任務還未執行的話, react 將會強制執行該任務
  // 初始化渲染時, 任務同步執行不涉及被打斷的問題 1073741823
  const expirationTime = computeExpirationForFiber(
    currentTime,
    current,
    suspenseConfig,
  );
  // 設置FiberRoot.context, 首次執行返回一個emptyContext, 是一個 {}
  const context = getContextForSubtree(parentComponent);
  // 初始渲染時 Fiber Root 對象中的 context 屬性值爲 null
  // 因此會進入到 if 中
  if (container.context === null) {
    // 初始渲染時將 context 屬性值設置爲 {}
    container.context = context;
  } else {
    container.pendingContext = context;
  }
  // 建立一個待執行任務
  const update = createUpdate(expirationTime, suspenseConfig);
  // 將要更新的內容掛載到更新對象中的 payload 中
  // 將要更新的組件存儲在 payload 對象中, 方便後期獲取
  update.payload = {element};
  // 判斷 callback 是否存在
  callback = callback === undefined ? null : callback;
  // 若是 callback 存在
  if (callback !== null) {
    // 將 callback 掛載到 update 對象中
    // 其實就是一層層傳遞 方便 ReactElement 元素渲染完成調用
    // 回調函數執行完成後會被清除 能夠在代碼的後面加上 return 進行驗證
    update.callback = callback;
  }
  // 將 update 對象加入到當前 Fiber 的更新隊列當中 (updateQueue)
  enqueueUpdate(current, update);
  // 調度和更新 current 對象
  scheduleWork(current, expirationTime);
  // 返回過時時間
  return expirationTime;
}

enqueueUpdate

文件位置: packages/react-reconciler/src/ReactUpdateQueue.js

// 將任務(Update)存放於任務隊列(updateQueue)中
// 建立單向鏈表結構存放 update, next 用來串聯 update
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // 獲取當前 Fiber 的 更新隊列
  const updateQueue = fiber.updateQueue;
  // 若是更新隊列不存在 就返回 null
  if (updateQueue === null) {
    // 僅發生在 fiber 已經被卸載
    return;
  }
  // 獲取待執行的 Update 任務
  // 初始渲染時沒有待執行的任務
  const sharedQueue = updateQueue.shared;
  const pending = sharedQueue.pending;
  // 若是沒有待執行的 Update 任務
  if (pending === null) {
    // 這是第一次更新, 建立一個循環列表.
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  // 將 Update 任務存儲在 pending 屬性中
  sharedQueue.pending = update;
}

scheduleWork as scheduleUpdateOnFiber

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

/**
 * 判斷任務是否爲同步 調用同步任務入口
 */
export function scheduleUpdateOnFiber(
  // rootFiber
  fiber: Fiber,
  expirationTime: ExpirationTime,
) {
  /**
   * fiber: 初始化渲染時爲 rootFiber, 即 <div id="root"></div> 對應的 Fiber 對象
   * expirationTime: 任務過時時間 =>1073741823
   */
  /**
   * 判斷是不是無限循環的 update 若是是就報錯
   * 在 componentWillUpdate 或者 componentDidUpdate 生命週期函數中重複調用
   * setState 方法時, 可能會發生這種狀況, React 限制了嵌套更新的數量以防止無限循環
   * 限制的嵌套更新數量爲 50, 可經過 NESTED_UPDATE_LIMIT 全局變量獲取
   */
  checkForNestedUpdates();
  // 判斷任務是不是同步任務 Sync的值爲: 1073741823
  if (expirationTime === Sync) {
    if (
      // 檢查是否處於非批量更新模式
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      // 檢查是否沒有處於正在進行渲染的任務
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
      // 同步任務入口點
      performSyncWorkOnRoot(root);
    }
  // 忽略了一些初始化渲染不會獲得執行的代碼
}

首次加載由於要讓用戶儘快看到界面 因此是一個同步任務
會走performSyncWorkOnRoot 這個同步任務入口

performSyncWorkOnRoot
正式進入render階段 開始構建workInProgress 樹

// 進入 render 階段, 構建 workInProgress Fiber 樹
function performSyncWorkOnRoot(root) {
  // 參數 root 爲 fiberRoot 對象
  // 檢查是否有過時的任務
  // 若是沒有過時的任務 值爲 0
  // 初始化渲染沒有過時的任務待執行
  const lastExpiredTime = root.lastExpiredTime;
  // NoWork 值爲 0
  // 若是有過時的任務 將過時時間設置爲 lastExpiredTime 不然將過時時間設置爲 Sync
  // 初始渲染過時時間被設置成了 Sync
  const expirationTime = lastExpiredTime !== NoWork ? lastExpiredTime : Sync;

  // 若是 root 和 workInProgressRoot 不相等
  // 說明 workInProgressRoot 不存在, 說明尚未構建 workInProgress Fiber 樹
  // workInProgressRoot 爲全局變量 默認值爲 null, 初始渲染時值爲 null
  // expirationTime => 1073741823
  // renderExpirationTime => 0
  // true
  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {
    // 構建 workInProgressFiber 樹及rootFiber
    prepareFreshStack(root, expirationTime);
  }
  // workInProgress 若是不爲 null
  if (workInProgress !== null) {
    do {
      try {
        // 以同步的方式開始構建 Fiber 對象
        workLoopSync();
        // 跳出 while 循環
        break;
      } catch (thrownValue) {
        handleError(root, thrownValue);
      }
    } while (true);
    
    if (workInProgress !== null) {
      // 這是一個同步渲染, 因此咱們應該完成整棵樹.
      // 沒法提交不完整的 root, 此錯誤多是因爲React中的錯誤所致. 請提出問題.
      invariant(
        false,
        'Cannot commit an incomplete root. This error is likely caused by a ' +
          'bug in React. Please file an issue.',
      );
    } else {
      // 將構建好的新 Fiber 對象存儲在 finishedWork 屬性中
      // 提交階段使用
      root.finishedWork = (root.current.alternate: any);
      root.finishedExpirationTime = expirationTime;
      // 結束 render 階段
      // 進入 commit 階段
      finishSyncRender(root);
    }
  }
}

prepareFreshStack

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js
構建 workInProgress Fiber 樹中的 rootFiber

/**
 * 根據 currentFiber 樹中的 rootFiber
 * 構建 workInProgressFiber 樹中的 rootFiber
 */
function prepareFreshStack(root, expirationTime) {
  // 爲 FiberRoot 對象添加 finishedWork 屬性
  // finishedWork 表示 render 階段執行完成後構建的待提交的 Fiber 對象
  root.finishedWork = null;
  // 初始化 finishedExpirationTime 值爲 0
  root.finishedExpirationTime = NoWork;

  // 建構 workInProgress Fiber 樹的 Fiber 對象
  workInProgressRoot = root;
  // 構建 workInProgress Fiber 樹中的 rootFiber
  workInProgress = createWorkInProgress(root.current, null);
  renderExpirationTime = expirationTime;
  workInProgressRootExitStatus = RootIncomplete;
}

createWorkInProgress

文件位置: packages/react-reconciler/src/ReactFiber.js

// 構建 workInProgress Fiber 樹中的 rootFiber
// 構建完成後會替換 current fiber
// 初始渲染 pendingProps 爲 null
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  // current: current Fiber 中的 rootFiber
  // 獲取 current Fiber 中對應的 workInProgress Fiber
  let workInProgress = current.alternate;
  // 若是 workInProgress 不存在
  if (workInProgress === null) {
    // 建立 fiber 對象
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    // 屬性複用
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;
    // 使用 alternate 存儲 current
    workInProgress.alternate = current;
    // 使用 alternate 存儲 workInProgress
    current.alternate = workInProgress;
  }
  
  workInProgress.childExpirationTime = current.childExpirationTime;
  workInProgress.expirationTime = current.expirationTime;
  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;
    
  // 返回建立好的 workInProgress Fiber 對象
  return workInProgress;
}

workLoopSync

根據rootFiber 開始構建 逐級構建 整顆 fiber樹 循環構建
文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

// 它的值不爲 null 意味着該 fiber 對象上仍然有更新要執行
  while (workInProgress !== null) {
    workInProgress = performUnitOfWork(workInProgress);
  }
}

performUnitOfWork

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

這裏是 承接 遞的過程從父到子 構建(child和兄弟) 以及 歸的過程 從子到父

function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
  // unitOfWork => workInProgress Fiber 樹中的 rootFiber
  // current => currentFiber 樹中的 rootFiber
  const current = unitOfWork.alternate;
  // 存儲下一個要構建的子級 Fiber 對象
  let next;
  // false
  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    // 初始渲染 不執行
  } else {
    // beginWork: 從父到子, 構建 Fiber 節點對象
    // 返回值 next 爲當前節點的子節點
    next = beginWork(current, unitOfWork, renderExpirationTime);
  }
  // 爲舊的 props 屬性賦值
  // 這次更新後 pendingProps 變爲 memoizedProps
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  // 若是子節點不存在說明當前節點向下遍歷子節點已經到底了
  // 繼續向上返回 遇到兄弟節點 構建兄弟節點的子 Fiber 對象 直到返回到根 Fiber 對象
  if (next === null) {
    // 從子到父, 構建其他節點 Fiber 對象
    next = completeUnitOfWork(unitOfWork);
  }
  return next;
}

beginWork

遞 從父到子
文件位置: packages/react-reconciler/src/ReactFiberBeginWork.js

// 從父到子, 構建 Fiber 節點對象
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  // NoWork 常量 值爲0 清空過時時間
  workInProgress.expirationTime = NoWork;
  // 根據當前 Fiber 的類型決定如何構建起子級 Fiber 對象
  // 文件位置: shared/ReactWorkTags.js
  switch (workInProgress.tag) {
    // 2
    // 函數型組件在第一次渲染組件時使用
    case IndeterminateComponent: {
      return mountIndeterminateComponent(
        // 舊 Fiber
        current,
        // 新 Fiber
        workInProgress,
        // 新 Fiber 的 type 值 初始渲染時是App組件函數
        workInProgress.type,
        // 同步 整數最大值 1073741823
        renderExpirationTime,
      );
    }
    // 0
    case FunctionComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateFunctionComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderExpirationTime,
      );
    }
    // 1
    case ClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderExpirationTime,
      );
    }
    // 3
    case HostRoot:
      return updateHostRoot(current, workInProgress, renderExpirationTime);
    // 5
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderExpirationTime);
    // 6
    case HostText:
      return updateHostText(current, workInProgress);
  // 組件類型未知 報錯
  invariant(
    false,
    'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +
      'React. Please file an issue.',
    workInProgress.tag,
  );
}

updateHostRoot

首次加載會匹配HostRoot 而後會執行updateHostRoot函數
文件位置: packages/react-reconciler/src/ReactFiberBeginWork.js

// HostRoot => <div id="root"></div> 對應的 Fiber 對象
// 找出 HostRoot 的子 ReactElement 併爲其構建 Fiber 對象
function updateHostRoot(current, workInProgress, renderExpirationTime) {
  // 獲取更新隊列
  const updateQueue = workInProgress.updateQueue;
  // 獲取新的 props 對象 null
  const nextProps = workInProgress.pendingProps;
  // 獲取上一次渲染使用的 state null
  const prevState = workInProgress.memoizedState;
  // 獲取上一次渲染使用的 children null
  const prevChildren = prevState !== null ? prevState.element : null;
  // 淺複製更新隊列, 防止引用屬性互相影響
  // workInProgress.updateQueue 淺拷貝 current.updateQueue
  cloneUpdateQueue(current, workInProgress);
  // 獲取 updateQueue.payload 並賦值到 workInProgress.memoizedState
  // 要更新的內容就是 element 就是 rootFiber 的子元素
  processUpdateQueue(workInProgress, nextProps, null, renderExpirationTime);
  // 獲取 element 所在對象
  const nextState = workInProgress.memoizedState;
  // 從對象中獲取 element
  const nextChildren = nextState.element;
  // 獲取 fiberRoot 對象
  const root: FiberRoot = workInProgress.stateNode;
  // 服務器端渲染走 if
  if (root.hydrate && enterHydrationState(workInProgress)) {
    // 忽略
  } else {
    // 客戶端渲染走 else
    // 構建子節點 fiber 對象
    reconcileChildren(
      current,
      workInProgress,
      nextChildren,
      renderExpirationTime,
    );
  }
  // 返回子節點 fiber 對象
  return workInProgress.child;
}

能夠看到該方法會調用reconcileChildren 構建子級和兄弟節點(和咱們mini-fiber相似)
最後返回子級
這個實現邏輯 https://segmentfault.com/a/11...
就是我這篇 mini-fiber的的構建過程
找到沒有子級以後,返回到父級 而後找父級的兄弟節點 再繼續重複這個構建過程

reconcileChildren

文件位置: packages/react-reconciler/src/ReactFiberBeginWork.js

export function reconcileChildren(
  // 舊 Fiber
  current: Fiber | null,
  // 父級 Fiber
  workInProgress: Fiber,
  // 子級 vdom 對象
  nextChildren: any,
  // 初始渲染 整型最大值 表明同步任務
  renderExpirationTime: ExpirationTime,
) {
  /**
   * 爲何要傳遞 current ?
   * 若是不是初始渲染的狀況, 要進行新舊 Fiber 對比
   * 初始渲染時則用不到 current
   */
  // 若是就 Fiber 爲 null 表示初始渲染
  if (current === null) {
    // 爲當前構建的 Fiber 對象添加子級 Fiber 對象
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderExpirationTime,
    );
  }
  // 忽略了 else 的狀況
}

mountChildFibers

文件位置: packages/react-reconciler/src/ReactChildFiber.js

/**
 * shouldTrackSideEffects 標識, 是否爲 Fiber 對象添加 effectTag
 * true 添加 false 不添加
 * 對於初始渲染來講, 只有根組件須要添加, 其餘元素不須要添加, 防止過多的 DOM 操做
 */
// 用於初始渲染
export const mountChildFibers = ChildReconciler(false);

function ChildReconciler(shouldTrackSideEffects) {
 
  function placeChild(
    newFiber: Fiber,
    lastPlacedIndex: number,
    newIndex: number,
  ): number {
    newFiber.index = newIndex;
    if (!shouldTrackSideEffects) {
      return lastPlacedIndex;
    }
    // 忽略了一部分初始化渲染不執行的代碼
  }

  function placeSingleChild(newFiber: Fiber): Fiber {
    // 若是是初始渲染 會在根組件(App)上設置 effectTag 屬性爲 Placement 值爲 1
    // 其餘子級節點具備默認值爲 0 防止在 commit 階段反覆操做真實DOM
    // 初始渲染時若是當前處理的是根組件 true 其餘組件 false
    if (shouldTrackSideEffects && newFiber.alternate === null) {
      // Placement 表示新建立的節點
      newFiber.effectTag = Placement;
    }
    return newFiber;
  }
  
  // 處理子元素是數組的狀況
  function reconcileChildrenArray(
    // 父級 Fiber
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    // 子級 vdom 數組
    newChildren: Array<*>,
    expirationTime: ExpirationTime,
  ): Fiber | null {
    /**
     * 存儲第一個子節點 Fiber 對象
     * 方法返回的也是第一個子節點 Fiber 對象
     * 由於其餘子節點 Fiber 對象都存儲在上一個子 Fiber 節點對象的 sibling 屬性中
     */
    let resultingFirstChild: Fiber | null = null;
    // 上一次建立的 Fiber 對象
    let previousNewFiber: Fiber | null = null;
    // 初始渲染沒有舊的子級 因此爲 null
    let oldFiber = currentFirstChild;

    let lastPlacedIndex = 0;
    let newIdx = 0;
    let nextOldFiber = null;

    // oldFiber 爲空 說明是初始渲染
    if (oldFiber === null) {
      // 遍歷子 vdom 對象
      for (; newIdx < newChildren.length; newIdx++) {
        // 建立子 vdom 對應的 fiber 對象
        const newFiber = createChild(
          returnFiber,
          newChildren[newIdx],
          expirationTime,
        );
        // 若是 newFiber 爲 null
        if (newFiber === null) {
          // 進入下次循環
          continue;
        }
        // 初始渲染時只爲 newFiber 添加了 index 屬性,
        // 其餘事沒幹. lastPlacedIndex 被原封不動的返回了
        lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
        // 爲當前節點設置下一個兄弟節點
        if (previousNewFiber === null) {
          // 存儲第一個子 Fiber 發生在第一次循環時
          resultingFirstChild = newFiber;
        } else {
          // 爲節點設置下一個兄弟 Fiber
          previousNewFiber.sibling = newFiber;
        }
        // 在循環的過程當中更新上一個建立的Fiber 對象
        previousNewFiber = newFiber;
      }
      // 返回建立好的子 Fiber
      // 其餘 Fiber 都做爲 sibling 存在
      return resultingFirstChild;
    }
    // 返回第一個子元素 Fiber 對象
    return resultingFirstChild;
  }

  // 處理子元素是文本或者數值的狀況
  function reconcileSingleTextNode(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    textContent: string,
    expirationTime: ExpirationTime,
  ): Fiber {
    // 初始渲染不執行
    if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
      // We already have an existing node so let's just update it and delete
      // the rest.
      deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
      const existing = useFiber(currentFirstChild, textContent);
      existing.return = returnFiber;
      return existing;
    }
    // 現有的第一個子節點不是文本節點,所以咱們須要建立一個並刪除現有的.
    // 初始渲染不執行
    deleteRemainingChildren(returnFiber, currentFirstChild);
    // 根據文本建立 Fiber 對象
    const created = createFiberFromText(
      textContent,
      returnFiber.mode,
      expirationTime,
    );
    // 設置父 Fiber 對象
    created.return = returnFiber;
    // 返回建立好的 Fiber 對象
    return created;
  }
  // 處理子元素是單個對象的狀況
  function reconcileSingleElement(
    // 父 Fiber 對象
    returnFiber: Fiber,
    // 備份子 fiber
    currentFirstChild: Fiber | null,
    // 子 vdom 對象
    element: ReactElement,
    expirationTime: ExpirationTime,
  ): Fiber {
    // 查看子 vdom 對象是否表示 fragment
    if (element.type === REACT_FRAGMENT_TYPE) {
      // false
    } else {
      // 根據 React Element 建立 Fiber 對象
      // 返回建立好的 Fiber 對象
      const created = createFiberFromElement(
        element,
        // 用來表示當前組件下的全部子組件要用處於何種渲染模式
        // 文件位置: ./ReactTypeOfMode.js
        // 0    同步渲染模式
        // 100  異步渲染模式
        returnFiber.mode,
        expirationTime,
      );
      // 添加 ref 屬性 { current: DOM }
      created.ref = coerceRef(returnFiber, currentFirstChild, element);
      // 添加父級 Fiber 對象
      created.return = returnFiber;
      // 返回建立好的子 Fiber
      return created;
    }
  }

  function reconcileChildFibers(
    // 父 Fiber 對象
    returnFiber: Fiber,
    // 舊的第一個子 Fiber 初始渲染 null
    currentFirstChild: Fiber | null,
    // 新的子 vdom 對象
    newChild: any,
    // 初始渲染 整型最大值 表明同步任務
    expirationTime: ExpirationTime,
  ): Fiber | null {
    // 這是入口方法, 根據 newChild 類型進行對應處理

    // 判斷新的子 vdom 是否爲佔位組件 好比 <></>
    // false
    const isUnkeyedTopLevelFragment =
      typeof newChild === 'object' &&
      newChild !== null &&
      newChild.type === REACT_FRAGMENT_TYPE &&
      newChild.key === null;

    // 若是 newChild 爲佔位符, 使用 佔位符組件的子元素做爲 newChild
    if (isUnkeyedTopLevelFragment) {
      newChild = newChild.props.children;
    }

    // 檢測 newChild 是否爲對象類型
    const isObject = typeof newChild === 'object' && newChild !== null;

    // newChild 是單個對象的狀況
    if (isObject) {
      // 匹配子元素的類型
      switch (newChild.$$typeof) {
        // 子元素爲 ReactElement
        case REACT_ELEMENT_TYPE:
          // 爲 Fiber 對象設置 effectTag 屬性
          // 返回建立好的子 Fiber
          return placeSingleChild(
            // 處理單個 React Element 的狀況
            // 內部會調用其餘方法建立對應的 Fiber 對象
            reconcileSingleElement(
              returnFiber,
              currentFirstChild,
              newChild,
              expirationTime,
            ),
          );
      }
    }
      
    // 處理 children 爲文本和數值的狀況 return "App works"
    if (typeof newChild === 'string' || typeof newChild === 'number') {
      return placeSingleChild(
        reconcileSingleTextNode(
          returnFiber,
          currentFirstChild,
          // 若是 newChild 是數值, 轉換爲字符串
          '' + newChild,
          expirationTime,
        ),
      );
    }

    // children 是數組的狀況
    if (isArray(newChild)) {
      // 返回建立好的子 Fiber
      return reconcileChildrenArray(
        returnFiber,
        currentFirstChild,
        newChild,
        expirationTime,
      );
    }
  }
}

completeUnitOfWork

下一步是從子到父
文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

/**
 *
 * 從下至上移動到該節點的兄弟節點, 若是一直往上沒有兄弟節點就返回父節點, 最終會到達 root 節點
 * 1. 建立其餘節點的 Fiber 對象
 * 2. 建立每個節點的真實 DOM 對象並將其添加到 stateNode 屬性中
 * 3. 構建 effect 鏈表結構
 */
function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
  // 爲 workInProgress 全局變量從新賦值
  workInProgress = unitOfWork;
  do {
    // 獲取備份節點
    // 初始化渲染 非根 Fiber 對象沒有備份節點 因此 current 爲 null
    const current = workInProgress.alternate;
    // 父級 Fiber 對象, 非根 Fiber 對象都有父級
    const returnFiber = workInProgress.return;
    // 判斷傳入的 Fiber 對象是否構建完成, 任務調度相關
    // & 是表示位的與運算, 把左右兩邊的數字轉化爲二進制
    // 而後每一位分別進行比較, 若是相等就爲1, 不相等即爲0
    // 此處應用"位與"運算符的目的是"清零"
    // true
    if ((workInProgress.effectTag & Incomplete) === NoEffect) {
      let next;
      // 若是不能使用分析器的 timer, 直接執行 completeWork
      // enableProfilerTimer => true
      // 但此處不管條件是否成立都會執行 completeWork
      if (
        !enableProfilerTimer ||
        (workInProgress.mode & ProfileMode) === NoMode
      ) {
        // 重點代碼(二)
        // 建立節點真實 DOM 對象並將其添加到 stateNode 屬性中
        next = completeWork(current, workInProgress, renderExpirationTime);
      } else {
        // 建立節點真實 DOM 對象並將其添加到 stateNode 屬性中
        next = completeWork(current, workInProgress, renderExpirationTime);
      }
      // 重點代碼(一)
      // 若是子級存在
      if (next !== null) {
        // 返回子級 一直返回到 workLoopSync
        // 再從新執行 performUnitOfWork 構建子級 Fiber 節點對象
        return next;
      }

      // 構建 effect 鏈表結構
      // 若是不是根 Fiber 就是 true 不然就是 false
      // 將子樹和此 Fiber 的全部 effect 附加到父級的 effect 列表中
      if (
        // 若是父 Fiber 存在 而且
        returnFiber !== null &&
        // 父 Fiber 對象中的 effectTag 爲 0
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        // 將子樹和此 Fiber 的全部反作用附加到父級的 effect 列表上

        // 如下兩個判斷的做用是蒐集子 Fiber的 effect 到父 Fiber
        if (returnFiber.firstEffect === null) {
          // first
          returnFiber.firstEffect = workInProgress.firstEffect;
        }

        if (workInProgress.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            // next
            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
          }
          // last
          returnFiber.lastEffect = workInProgress.lastEffect;
        }

        // 獲取反作用標記
        // 初始渲染時除[根組件]之外的 Fiber, effectTag 值都爲 0, 即不須要執行任何真實DOM操做
        // 根組件的 effectTag 值爲 3, 即須要將此節點對應的真實DOM對象添加到頁面中
        const effectTag = workInProgress.effectTag;

        // 建立 effect 列表時跳過 NoWork(0) 和 PerformedWork(1) 標記
        // PerformedWork 由 React DevTools 讀取, 不提交
        // 初始渲染時 只有遍歷到了根組件 判斷條件才能成立, 將 effect 鏈表添加到 rootFiber
        // 初始渲染 FiberRoot 對象中的 firstEffect 和 lastEffect 都是 App 組件
        // 由於當全部節點在內存中構建完成後, 只須要一次將全部 DOM 添加到頁面中
        if (effectTag > PerformedWork) {
          // false
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress;
          } else {
            // 爲 fiberRoot 添加 firstEffect
            returnFiber.firstEffect = workInProgress;
          }
          // 爲 fiberRoot 添加 lastEffect
          returnFiber.lastEffect = workInProgress;
        }
      }
    } else {
        // 忽略了初始渲染不執行的代碼      
    }
    // 獲取下一個同級 Fiber 對象
    const siblingFiber = workInProgress.sibling;
    // 若是下一個同級 Fiber 對象存在
    if (siblingFiber !== null) {
      // 返回下一個同級 Fiber 對象
      return siblingFiber;
    }
    // 不然退回父級
    workInProgress = returnFiber;
  } while (workInProgress !== null);

  // 當執行到這裏的時候, 說明遍歷到了 root 節點, 已完成遍歷
  // 更新 workInProgressRootExitStatus 的狀態爲 已完成
  if (workInProgressRootExitStatus === RootIncomplete) {
    workInProgressRootExitStatus = RootCompleted;
  }
  return null;
}

completeWork

文件位置: packages/react-reconciler/src/ReactFiberCompleteWork.js

首次加載 歸的時候 會把子級dom結構追加到父級中

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  // 獲取待更新 props
  const newProps = workInProgress.pendingProps;
  switch (workInProgress.tag) {
    // 0
    case FunctionComponent:
      return null;
    // 3
    case HostRoot: {
      updateHostContainer(workInProgress);
      return null;
    }
    // 5
    case HostComponent: {
      // 獲取 rootDOM 節點 <div id="root"></div>
      const rootContainerInstance = getRootHostContainer();
      // 節點的具體的類型 div span ...
      const type = workInProgress.type;
      // false current = null
      if (current !== null && workInProgress.stateNode != null) {
       // 初始渲染不執行
      } else {
                // false
        if (wasHydrated) {
         // 初始渲染不執行
        } else {
          // 建立節點實例對象 <div></div> <span></span>
          let instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );
          /**
           * 將全部的子級追加到父級中
           * instance 爲父級
           * workInProgress.child 爲子級
           */
          appendAllChildren(instance, workInProgress, false, false);
          // 爲 Fiber 對象添加 stateNode 屬性
          workInProgress.stateNode = instance;
        }
        // 處理 ref DOM 引用
        if (workInProgress.ref !== null) {
          markRef(workInProgress);
        }
      }
      return null;
    } 
  }
}

appendAllChildren

文件位置: packages/react-reconciler/src/ReactFiberCompleteWork.js

appendAllChildren = function (
    parent: Instance,
    workInProgress: Fiber,
    needsVisibilityToggle: boolean,
    isHidden: boolean,
  ) {
    // 獲取子級
    let node = workInProgress.child;
    // 若是子級不爲空 執行循環
    while (node !== null) {
      // 若是 node 是普通 ReactElement 或者爲文本
      if (node.tag === HostComponent || node.tag === HostText) {
        // 將子級追加到父級中
        appendInitialChild(parent, node.stateNode);
      } else if (node.child !== null) {
        // 若是 node 不是普通 ReactElement 又不是文本
        // 將 node 視爲組件, 組件自己不能轉換爲真實 DOM 元素
        // 獲取到組件的第一個子元素, 繼續執行循環
        node.child.return = node;
        node = node.child;
        continue;
      }
      // 若是 node 和 workInProgress 是同一個節點
      // 說明 node 已經退回到父級 終止循環
      // 說明此時全部子級都已經追加到父級中了
      if (node === workInProgress) {
        return;
      }
      // 處理子級節點的兄弟節點
      while (node.sibling === null) {
        // 若是節點沒有父級或者節點的父級是本身, 退出循環
        // 說明此時全部子級都已經追加到父級中了
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        // 更新 node
        node = node.return;
      }
      // 更新父級 方便回退
      node.sibling.return = node.return;
      // 將 node 更新爲下一個兄弟節點
      node = node.sibling;
    }
  };

歸的過程完了以後 總體的dom結構 會被所有 收集全 到頂級的fiber中

如今到這裏workInProgress 樹構建完了 也就是說render階段 已經結束了 接下來就是進入commit提交階段了,下面

finishSyncRender

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

function finishSyncRender(root) {
  // 銷燬 workInProgress Fiber 樹
  // 由於待提交 Fiber 對象已經被存儲在了 root.finishedWork 中
  workInProgressRoot = null;
  // 進入 commit 階段
  commitRoot(root);
}

commitRoot

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

開始正式提交commit

function commitRoot(root) {
  // 獲取任務優先級 97 => 普通優先級
  const renderPriorityLevel = getCurrentPriorityLevel();
  // 使用最高優先級執行當前任務, 由於 commit 階段不能夠被打斷
  // ImmediatePriority, 優先級爲 99, 最高優先級
  runWithPriority(
    ImmediatePriority,
    commitRootImpl.bind(null, root, renderPriorityLevel),
  );
  return null;
}

commitRootImpl

這裏開始執行commitRootImpl方法

commit 階段能夠分爲三個子階段:

  • before mutation 階段(執行 DOM 操做前)
  • mutation 階段(執行 DOM 操做)
  • layout 階段(執行 DOM 操做後)

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

function commitRootImpl(root, renderPriorityLevel) {
  // 獲取待提交 Fiber 對象 rootFiber
  const finishedWork = root.finishedWork;
  // 若是沒有任務要執行
  if (finishedWork === null) {
    // 阻止程序繼續向下執行
    return null;
  }
  // 重置爲默認值
  root.finishedWork = null;
  root.callbackNode = null;
  root.callbackExpirationTime = NoWork;
  root.callbackPriority = NoPriority;
  root.nextKnownPendingLevel = NoWork;
  
  // 獲取要執行 DOM 操做的反作用列表
  let firstEffect = finishedWork.firstEffect;

  // true
  if (firstEffect !== null) {
    // commit 第一個子階段
    nextEffect = firstEffect;
    // 處理類組件的 getSnapShotBeforeUpdate 生命週期函數
    do {
      invokeGuardedCallback(null, commitBeforeMutationEffects, null);
    } while (nextEffect !== null);
    
        // commit 第二個子階段
    nextEffect = firstEffect;
    do {
      invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);
    } while (nextEffect !== null);
    // 將 workInProgress Fiber 樹變成 current Fiber 樹
    root.current = finishedWork;
    
        // commit 第三個子階段
    nextEffect = firstEffect;
    do {
      invokeGuardedCallback(null, commitLayoutEffects, null, root,expirationTime);
    } while (nextEffect !== null);
        
    // 重置 nextEffect
    nextEffect = null;
  }
}

第一子階段

commitBeforeMutationEffects

件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

// commit 階段的第一個子階段
// 調用類組件的 getSnapshotBeforeUpdate 生命週期函數
function commitBeforeMutationEffects() {
  // 循環 effect 鏈
  while (nextEffect !== null) {
    // nextEffect 是 effect 鏈上從 firstEffect 到 lastEffec 的每個須要commit的 fiber 對象

    // 初始化渲染第一個 nextEffect 爲 App 組件
    // effectTag => 3
    const effectTag = nextEffect.effectTag;
    // console.log(effectTag);
    // nextEffect = null;
    // return;

    // 若是 fiber 對象中裏有 Snapshot 這個 effectTag 的話
    // Snapshot 和更新有關係 初始化渲染 不執行
    // false
    if ((effectTag & Snapshot) !== NoEffect) {
      // 獲取當前 fiber 節點
      const current = nextEffect.alternate;
      // 當 nextEffect 上有 Snapshot 這個 effectTag 時
      // 執行如下方法, 主要是類組件調用 getSnapshotBeforeUpdate 生命週期函數
      commitBeforeMutationEffectOnFiber(current, nextEffect);
    }
    // 更新循環條件
    nextEffect = nextEffect.nextEffect;
  }
}

commitBeforeMutationEffectOnFiber as commitBeforeMutationLifeCycles

文件位置: packages/react-reconciler/src/ReactFiberCommitWork.js

function commitBeforeMutationLifeCycles(
  current: Fiber | null,
  finishedWork: Fiber,
): void {
  switch (finishedWork.tag) {
    case FunctionComponent:
    case ForwardRef:
    case SimpleMemoComponent:
    case Block: {
      return;
    }
    // 若是該 fiber 類型是 ClassComponent
    case ClassComponent: {
      if (finishedWork.effectTag & Snapshot) {
        if (current !== null) {
          // 舊的 props
          const prevProps = current.memoizedProps;
          // 舊的 state
          const prevState = current.memoizedState;
          // 獲取 classComponent 組件的實例對象
          const instance = finishedWork.stateNode;
          // 執行 getSnapshotBeforeUpdate 生命週期函數
          // 在組件更新前捕獲一些 DOM 信息
          // 返回自定義的值或 null, 統稱爲 snapshot
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
        }
      }
      return;
    }
    case HostRoot:
    case HostComponent:
    case HostText:
    case HostPortal:
    case IncompleteClassComponent:
      // Nothing to do for these component types
      return;
  }
}

第二子階段

commitMutationEffects

文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

// commit 階段的第二個子階段
// 根據 effectTag 執行 DOM 操做
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
  // 循環 effect 鏈
  while (nextEffect !== null) {
    // 獲取 effectTag
    // 初始渲染第一次循環爲 App 組件
    // 即將根組件及內部全部內容一次性添加到頁面中
    const effectTag = nextEffect.effectTag;

    // 根據 effectTag 分別處理
    let primaryEffectTag =
      effectTag & (Placement | Update | Deletion | Hydrating);
    // 匹配 effectTag
    // 初始渲染 primaryEffectTag 爲 2 匹配到 Placement
    switch (primaryEffectTag) {
      // 針對該節點及子節點進行插入操做
      case Placement: {
        commitPlacement(nextEffect);
        // effectTag 從 3 變爲 1
        // 從 effect 標籤中清除 "placement" 重置 effectTag 值
        // 以便咱們知道在調用諸如componentDidMount之類的任何生命週期以前已將其插入。
        nextEffect.effectTag &= ~Placement;
        break;
      }
      // 插入並更新 DOM
      case PlacementAndUpdate: {
        // 插入
        commitPlacement(nextEffect);
        nextEffect.effectTag &= ~Placement;

        // 更新
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      // 服務器端渲染
      case Hydrating: {
        nextEffect.effectTag &= ~Hydrating;
        break;
      }
      // 服務器端渲染
      case HydratingAndUpdate: {
        nextEffect.effectTag &= ~Hydrating;

        // Update
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      // 更新 DOM
      case Update: {
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      // 刪除 DOM
      case Deletion: {
        commitDeletion(root, nextEffect, renderPriorityLevel);
        break;
      }
    }
    nextEffect = nextEffect.nextEffect;
  }
}
相關文章
相關標籤/搜索