首先 我這裏 有一份配置好的 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。數組
文件位置: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 */ 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 解析出來大概是這樣app
能夠參考我以前寫的解析出來的結構 是相似的
而後就是咱們轉換出來的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函數
文件位置: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方法
這個方法用來生成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 方法。
這個方法將任務(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; }
文件位置: 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; }
文件位置: 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); } } }
文件位置: 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; }
文件位置: 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; }
根據rootFiber 開始構建 逐級構建 整顆 fiber樹 循環構建
文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js
// 它的值不爲 null 意味着該 fiber 對象上仍然有更新要執行 while (workInProgress !== null) { workInProgress = performUnitOfWork(workInProgress); } }
文件位置: 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; }
遞 從父到子
文件位置: 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, ); }
首次加載會匹配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的的構建過程
找到沒有子級以後,返回到父級 而後找父級的兄弟節點 再繼續重複這個構建過程
文件位置: 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 的狀況 }
文件位置: 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, ); } } }
下一步是從子到父
文件位置: 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; } } }
文件位置: 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提交階段了,下面
文件位置: packages/react-reconciler/src/ReactFiberWorkLoop.js
function finishSyncRender(root) { // 銷燬 workInProgress Fiber 樹 // 由於待提交 Fiber 對象已經被存儲在了 root.finishedWork 中 workInProgressRoot = null; // 進入 commit 階段 commitRoot(root); }
文件位置: 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方法
commit 階段能夠分爲三個子階段:
文件位置: 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; } }
第一子階段
件位置: 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; } }
文件位置: 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; } }
第二子階段
文件位置: 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; } }