往期回顧
React 源碼解析 React 的更新
React 源碼解析 - React 創(chuàng)建更新回顧和 React 的批量更新
React 源碼解析 - 調(diào)度模塊原理 - 實(shí)現(xiàn) requestIdleCallback ?
React 源碼解析 - reactScheduler 異步任務(wù)調(diào)度
renderRoot 入口
- ReactFiberScheduler.js
function renderRoot(root: FiberRoot, isYieldy: boolean, isExpired: boolean) {
// ...
if ( // 將要執(zhí)行的任務(wù) root 和 expirationTime 和 nextRenderExpirationTime篮灼、nextRoot 預(yù)期的不一樣怀吻, 應(yīng)該是之前任務(wù)被高優(yōu)先級(jí)的任務(wù)打斷了。
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null // 更新結(jié)束 fiber 的 child佛玄,下一個(gè)節(jié)點(diǎn), 首次 = null
) {
// 初始化的內(nèi)容
resetStack(); // 重置
nextRoot = root;
nextRenderExpirationTime = expirationTime; // root.nextExpirationTimeToWorkOn;
nextUnitOfWork = createWorkInProgress( // 拷貝了一份 fiber 對(duì)象操作
nextRoot.current,
null,
nextRenderExpirationTime,
);
root.pendingCommitExpirationTime = NoWork; // 設(shè)置成 NoWork
// ...
}
// 開始進(jìn)入 workLoop
do {
try {
workLoop(isYieldy); // 進(jìn)行每個(gè)節(jié)點(diǎn)的更新
} catch (thrownValue) {
// ...
break; // 遇到了某種錯(cuò)誤跳出
} while(true)
}
workLoop 中所有發(fā)生的錯(cuò)誤都會(huì)被 render 階段 catch雳刺,render 階段會(huì)根據(jù)捕獲的錯(cuò)誤具體內(nèi)容進(jìn)行相應(yīng)的操作
function workLoop(isYieldy) {
if (!isYieldy) { // 不可中斷 Sync 和 超時(shí)任務(wù)不可中斷
// Flush work without yielding
// nextUnitOfWork 是 fiber 對(duì)象,為 null 已經(jīng)是 root 節(jié)點(diǎn) fiber return 的 null 了
// 用于記錄render階段Fiber樹遍歷過程中下一個(gè)需要執(zhí)行的節(jié)點(diǎn)。在resetStack中分別被重置,他只會(huì)指向workInProgress
while (nextUnitOfWork !== null) { // 不停的更新目锭,不為 null 就不停執(zhí)行 next 的 child 的更新
nextUnitOfWork = performUnitOfWork(nextUnitOfWork); // 進(jìn)行更新
}
} else {
// Flush asynchronous work until the deadline runs out of time.
while (nextUnitOfWork !== null && !shouldYield()) { // 判斷 shouldYield = false 當(dāng)前時(shí)間片是否有時(shí)間更新
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
workLoop 只是根據(jù)時(shí)間片是否有任務(wù)調(diào)用 performUnitOfWork 進(jìn)行更新, 只有當(dāng) nextUnitOfWork === null 時(shí)才代表任務(wù)已經(jīng)更新完了
performUnitOfWork 在 beginWork 中對(duì)當(dāng)前 fiber 進(jìn)行更新,更新到此 fiber 的最后時(shí)會(huì)去找兄弟節(jié)點(diǎn)纷捞,最后返回給 workLoop 中的 while(nextUnitOfWork) 中繼續(xù)執(zhí)行
beginWork
核心功能
- 驗(yàn)證當(dāng)前 fiber 樹是否需要更新
- 更新傳入的節(jié)點(diǎn)類型進(jìn)行對(duì)應(yīng)的更新
- 更新后調(diào)和子節(jié)點(diǎn)
第一步驗(yàn)證當(dāng)前 fiber 樹是否需要更新
- 比較當(dāng)前節(jié)點(diǎn) props 是否有變化
- 檢查當(dāng)前節(jié)點(diǎn)是否有更新或是否比當(dāng)前 root 的更新優(yōu)先級(jí)大
- 沒更新或優(yōu)先級(jí)低就跳過痢虹,bailoutOnAlreadyFinishedWork
-
bailoutOnAlreadyFinishedWork 可以判斷 current 是否有 child 更新
-
bailoutOnAlreadyFinishedWork 會(huì)判斷這個(gè) fiber 的子樹是否需要更新,如果有需要更新會(huì) clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 否則為 null
根據(jù) fiber 的 tag 類型進(jìn)行更新
進(jìn)行更新先把當(dāng)前 fiber 的 expirationTime 設(shè)置為 NoWork主儡,根據(jù) tag 進(jìn)行不同組件的更新
- ClassComponent: class 組件
- HostRoot: <div id="root" />
- HostComponent: html 標(biāo)簽
- HostText: 文本內(nèi)容
...
workInProgress 更新所用到的 fiber 對(duì)象屬性
- type
當(dāng)函數(shù)組件時(shí)是 function
當(dāng)為 class 組件時(shí)就是 class 構(gòu)造函數(shù)
當(dāng) dom 原生組件時(shí)就是標(biāo)簽 div 這種字符串 - pendingProps
fiber 更新帶來的新 props
更新函數(shù)會(huì)用到的參數(shù)
- current
workInProgress.alternate奖唯,指向當(dāng)前 fiber 沒更新的對(duì)象 - Component
workInProgress.type,此時(shí) fiber 節(jié)點(diǎn)組件的類型糜值,function丰捷,class坯墨,標(biāo)簽 字符串 - nextProps
workInProgress.pendingProps,此次更新帶來的新 props - renderExpirationTime
fiberRoot.expirationTime病往,fiberRoot 上最大優(yōu)先級(jí)的值
FunctionComponent 更新
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderExpirationTime,
) {
// ... context 相關(guān)
let nextChildren;
// Component 組件方法捣染,這里就是我們聲明組件的方式 function(props, context) {}
nextChildren = Component(nextProps, context);
// 把 nextChildren 這些 ReactElement 變成 Fiber 對(duì)象, 在 workInProgress.child 掛載 fiber
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
reconcileChildren
核心點(diǎn)
- 根據(jù) props.children 生成 fiber 子樹
- 判斷 fiber 對(duì)象是否可以復(fù)用, 在第一次渲染就渲染了 fiber 子樹,state 變化可能會(huì)導(dǎo)致不能復(fù)用停巷,但是大部分是可以復(fù)用的
- 列表根據(jù) key 優(yōu)化
根據(jù)首次渲染或更新渲染進(jìn)行操作
- current === null 首次渲染, mountChildFibers
-
current !== null 更新渲染, reconcileChildFibers
- mountChildFibers 和 reconcileChildFibers 都是由 ChildReconciler 返回的函數(shù)耍攘,區(qū)別只在參數(shù)不同
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);
reconcileChildFibers
- reconcileChildFibers 是 ChildReconciler 返回的最終函數(shù)
- 先判斷 newChild 是不是 Fragment 節(jié)點(diǎn)
- typeof newChild === object 是函數(shù)組件和 class 組件返回的 jsx - reconcileSingleElement
- typeof newChild === string 是 textNode - reconcileSingleTextNode
- 判斷 newChild 是個(gè)數(shù)組
- 判斷是 iterator 函數(shù)
- 都不符合拋錯(cuò)
- 提醒組件沒有合法的返回值
-
最后刪除所有的節(jié)點(diǎn) return deleteRemainingChildren(returnFiber: new, currentFirstChild: old);
placeSingleChild
更新渲染時(shí) placeSingleChild 會(huì)把新創(chuàng)建的 fiber 節(jié)點(diǎn)標(biāo)記為 Placement, 待到提交階段處理
其中 ReactElement, Portal, TextNode 三種類型的節(jié)點(diǎn)需要進(jìn)行處理x
reconcileSingleElement 更新 ReactElement
調(diào)和單個(gè)子節(jié)點(diǎn)
第一段邏輯,從原 fiber 節(jié)點(diǎn)的兄弟節(jié)點(diǎn)遍歷畔勤,比較 fiber 節(jié)點(diǎn)和 nextChilren key 值
- 符合復(fù)用條件蕾各,標(biāo)記此 fiber 節(jié)點(diǎn)的所有兄弟節(jié)點(diǎn) effect 在提交階段刪除達(dá)到只復(fù)用干凈的這個(gè) fiber 節(jié)點(diǎn)的目的,返回這個(gè)可以復(fù)用的節(jié)點(diǎn)
- 如果 key 相等庆揪,不符合復(fù)用條件直接跳出, 進(jìn)入第二段邏輯
- 如果 key 不相等逐漸標(biāo)記刪除遍歷的 fiber 節(jié)點(diǎn), 進(jìn)入第二段邏輯
- 這里調(diào)和單個(gè)子節(jié)點(diǎn), 如果 key 不存在為 null 我們也認(rèn)為他是相等的示损,判斷 type 和 elementType 來看他們是否一是個(gè)組件函數(shù)
-
deleteChild 標(biāo)記刪除
-
deleteRemainingChildren 刪除多余的兄弟節(jié)點(diǎn)
第二段邏輯,沒有可以復(fù)用的節(jié)點(diǎn)嚷硫,根據(jù) elment nextChildren 的類型創(chuàng)建 Fragment 或者 Element 類型的 fiber。
reconcileSingleTextNode 更新 textNode
- currentFirstChild 原 fiber 節(jié)點(diǎn) child 是文本節(jié)點(diǎn)符合復(fù)用條件
- currentFirstChild 不是文本節(jié)點(diǎn)始鱼,現(xiàn)在要更新為文本節(jié)點(diǎn)刪除后重新創(chuàng)建
useFiber 創(chuàng)建復(fù)用的節(jié)點(diǎn)
reconcileChildrenArray 調(diào)和數(shù)組 children
- react 新老 children 對(duì)比的過程算法
- 盡量減少節(jié)點(diǎn)的遍歷次數(shù)來達(dá)到判斷節(jié)點(diǎn)是否可復(fù)用的過程
第一次遍歷(優(yōu)化加速)
- 找到新老節(jié)點(diǎn)中不能復(fù)用的節(jié)點(diǎn)才跳出
- 判斷新老節(jié)點(diǎn)的 index
- 判斷新老 key 是否相同來復(fù)用
- 不能復(fù)用就 break 跳出當(dāng)前遍歷
-
能復(fù)用就構(gòu)建鏈表結(jié)構(gòu)
updateSlot
- 根據(jù) newChild 的類型和 oldChild.key 進(jìn)行判斷操作
- 返回 null 表示后面都不能復(fù)用了直接跳出
textNode 文本節(jié)點(diǎn)
-
oldFiber 不是 textNode 還有 key 值仔掸,是在數(shù)組里的,新的 textNode 不能復(fù)用返回 null
-
oldFiber 不是 textNode 創(chuàng)建新的 textNode 否則直接更新 textNode 內(nèi)容
ReactElement 節(jié)點(diǎn)和 isArray 數(shù)組節(jié)點(diǎn)
-
ReactElement 時(shí) updateElement
Fragment 時(shí)與 ReactElement 的處理相似医清,復(fù)用處理的內(nèi)容為 newChild.props.children
break 或者遍歷完畢后的情況
- newIdx === newChildren.length
- 新的 children 已經(jīng)在 updateSlot 中創(chuàng)建新的對(duì)象了, 新數(shù)組操作完成了, 所有新節(jié)點(diǎn)都已經(jīng)創(chuàng)建
- oldFiber === null
- 老的已經(jīng)被復(fù)用完了, 新的節(jié)點(diǎn)還有部分沒有創(chuàng)建, 找到最后沒有能復(fù)用的節(jié)點(diǎn)了
- 直接創(chuàng)建剩下的新節(jié)點(diǎn)構(gòu)建鏈表
情況一
- 按新數(shù)組 newChildren.length 的長(zhǎng)度遍歷完了
- 這時(shí) updateSlot 沒有返回 null起暮,所有節(jié)點(diǎn)都復(fù)用或新建的,都標(biāo)記好了位置
-
這個(gè)情況是最快的会烙,如果 oldFiber 老節(jié)點(diǎn)還有沒遍歷完的就刪掉
情況二
- 老的節(jié)點(diǎn)已經(jīng)被復(fù)用完了, 新的節(jié)點(diǎn)還有部分沒有創(chuàng)建, 遍歷到最后沒有能復(fù)用的節(jié)點(diǎn)了
-
newChildren 剩下的節(jié)點(diǎn)就直接創(chuàng)建负懦,同時(shí)進(jìn)行同樣的 place 標(biāo)記構(gòu)建鏈表結(jié)構(gòu)
核心通用操作,構(gòu)建 map 復(fù)用
- newChildren 沒有創(chuàng)建完柏腻,oldFiber 又有兄弟節(jié)點(diǎn)纸厉,數(shù)組存在順序的變
- 根據(jù)老節(jié)點(diǎn)的 key 或 index 構(gòu)建 map 對(duì)象
- 遍歷剩下的 newChildren
- 根據(jù) key 或 index 直接在 map 里找可以復(fù)用的對(duì)象或創(chuàng)建新的對(duì)象
-
再構(gòu)建鏈表
更新 classComponent 組件
- 首次渲染 instance === null
- constructClassInstance 生成實(shí)例
- mountClassInstance 掛載實(shí)例
- 渲染被中斷 instance !== null, current === null
- resumeMountClassInstance 復(fù)用實(shí)例但還是調(diào)用首次渲染的生命周期
- 更新渲染 instance !== null, current !== null
- updateClassInstance,調(diào)用 didUpdate 和 componentWillReceiveProp 生命周期
- 都是復(fù)用或創(chuàng)建 instance五嫂,通過 processUpdateQueue 計(jì)算新的 state 賦值到 fiber workInProgress.memoizedState 和 instance 上面記錄
- 最終執(zhí)行 finishClassComponent, 進(jìn)行錯(cuò)誤判斷的處理和判斷是否可以跳過更新的過程颗品,重新調(diào)和子節(jié)點(diǎn) reconcileChildren
首次渲染 class 組件
- instance === null
constructClassInstance 創(chuàng)建實(shí)例
- instance 賦值在 workInProgress.stateNode 上
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
renderExpirationTime: ExpirationTime,
): any {
// ...
// 從這里開始,ctor 就是 element.type 的 Compnent沃缘,這里生成 class 組件實(shí)例
const instance = new ctor(props, context);
const state = (workInProgress.memoizedState = // memoizedState 為實(shí)例的 state, 沒有就為 null
instance.state !== null && instance.state !== undefined
? instance.state
: null);
adoptClassInstance(workInProgress, instance);
// ...
return instance;
}
- 為 instance.updater 賦值 classComponentUpdater, 用于組件通過何種方式進(jìn)行 ReactDOM.render 和 setState 進(jìn)行更新
// 為實(shí)例確定 updater 對(duì)象
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = classComponentUpdater; // 給 class 組件實(shí)例的 updater 設(shè)置
workInProgress.stateNode = instance; // instance 賦值給當(dāng)前 workInProgress.stateNode
ReactInstanceMap.set(instance, workInProgress); // 給 instance._reactInternalFiber 賦值當(dāng)前的 fiber 對(duì)象
}
mountClassInstance 首次掛載實(shí)例
- 初始化 class 組件創(chuàng)建 updateQueue 計(jì)算更新 state
- 判斷和執(zhí)行 getDerivedStateFromProps, componentWillMount 生命周期躯枢,都會(huì)更新當(dāng)前 state
- 可以看到 componentWillMount 完全可以進(jìn)行 setState,會(huì)創(chuàng)建 updateQueue 計(jì)算更新當(dāng)前 state
- 最后標(biāo)記 componentDidMount 生命周期槐臀,待到提交階段更新完 dom 后執(zhí)行
processUpdateQueue 計(jì)算更新 state
-
updateQueue 的更新都是通過 baseState 計(jì)算的锄蹂,執(zhí)行 queue 的更新會(huì)檢查這次更新的優(yōu)先級(jí),優(yōu)先級(jí)低待到下次更新
-
每個(gè) update 都會(huì)計(jì)算出當(dāng)前的 state 結(jié)果水慨,如果 setState 有第二個(gè)參數(shù) callback 會(huì)標(biāo)記 effect 待到提交階段執(zhí)行得糜,這樣 callback 就能得到準(zhǔn)確的 state
-
getStateFromUpdate 根據(jù) update.tag 計(jì)算 state 的結(jié)果敬扛,會(huì)判斷 setState 傳入的函數(shù)或?qū)ο髢煞N情況
1 函數(shù)時(shí)會(huì)指定上下文,傳入 prevState, nextProps
2 對(duì)象時(shí)就是最終要更新的 state 對(duì)象
3 最后通過 Object.assign 生成新的 state 對(duì)象
resumeMountClassInstance 復(fù)用實(shí)例
-
中斷后恢復(fù)的組件復(fù)用實(shí)例仍然按新組件掛載來執(zhí)行
-
檢查得到 shouldUpdate掀亩,執(zhí)行 willMount 和標(biāo)記 didMount
-
shouldUpdate 由組件的 shouldComponentUpdate 判斷舔哪,pureComponent 會(huì)自動(dòng)比較 props
updateClassInstance 更新實(shí)例
-
過程與 resumeMountClassInstance 相似, 不過執(zhí)行的是 willUpdate, 標(biāo)記 didUpdate, getSnapshotBeforeUpdate
finishClassComponent 完成 class 組件更新
沒錯(cuò)誤捕獲又不需要更新
- 沒錯(cuò)誤捕獲也沒更新直接跳過
-
effect 的錯(cuò)誤標(biāo)記會(huì)在外側(cè) catch 中添加
捕獲錯(cuò)誤的操作
- class 組件沒有 getDerivedStateFromError, nextChildren = null
- class 組件有 getDerivedStateFromError 槽棍,直接執(zhí)行 instance.render() 獲得最新的 nextChildren, getDerivedStateFromError 在函數(shù)外 catch 到錯(cuò)誤并且執(zhí)行立即更新為正確的 state, 所以可以執(zhí)行 instance.render()
-
沒捕獲錯(cuò)誤 執(zhí)行 instance.render()
最后執(zhí)行的 reconcileChildren
IndeterminateComponent 更新
-
fiber 剛創(chuàng)建的時(shí)候 fiberTag 都為 IndeterminateComponent 類型捉蚤,只有當(dāng) class 組件有 construct 才為 class 組件類型
-
符合 class 組件條件按 class 組件更新
-
否則就按函數(shù)組件類型更新
- 只存在于首次更新的時(shí)候,只有首次更新的時(shí)候不確定 fiberTag 類型
更新 HostComponent 原生 dom 標(biāo)簽
- 原生標(biāo)簽 小寫的 標(biāo)簽
- 判斷標(biāo)簽內(nèi)容是不是純文本
- 是純文本沒子節(jié)點(diǎn)炼七,不是純文本根據(jù)之前的 props 標(biāo)記更新
- 跟 classCompnent 一樣有 makeRef 能使用 ref
updateHostComponent
- dom 標(biāo)簽內(nèi)是純文本 nextChildren 為 null缆巧,直接渲染文本內(nèi)容
- 原 props 是文本,現(xiàn)在換成節(jié)點(diǎn)標(biāo)記 effect 提交階段操作
- 和 classComponent 一樣可以使用 ref 的原因是都有 markRef
- 判斷 concurrentMode 異步組件是否有 hidden 屬性豌拙,異步組件 hidden 永不更新
- 最后進(jìn)行 reconcileChildren
shouldSetTextContent
- 特殊標(biāo)簽 textarea陕悬,option,noscript 直接渲染文本
- props.children 為 string按傅,number 直接渲染
- dangerouslySetInnerHTML 屬性 直接渲染
HostText 文本節(jié)點(diǎn)
updateHostText
文本內(nèi)容不需要構(gòu)建 fiber 結(jié)構(gòu)捉超,直接在提交階段更新就行了
function updateHostText(current, workInProgress) {
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
// Nothing to do here. This is terminal. We'll do the completion step
// immediately after.
return null; // 文本沒有子節(jié)點(diǎn)不需要調(diào)和, 到 提交階段直接就更新了
}
ForwardRef 更新
- 實(shí)現(xiàn)了 React.forwardRef((props, ref) => { 傳入了 ref }) 傳遞 ref 的功能
Mode 組件
- React 提供的組件
- <ConCurrentmode /> 標(biāo)簽
- <StaticMode /> 標(biāo)簽
-
updateMode 執(zhí)行,const nextChildren = workInProgress.pendingProps.children;
Memo 組件
- functionComponent 具有 pureComponent 功能
- memo 組件 Component.type 就是 傳入的function組件唯绍, memo(function(props) {})
- memo 組件的 props 都要作用于 function 組件內(nèi)
- memo 組件意義不多只是進(jìn)行了一次包裹的比較
- 創(chuàng)建的 child 沒有調(diào)和 reconcileChildren
初次渲染
- 根據(jù) React.memo() 傳入的函數(shù)組件進(jìn)行判斷
- SimpleFunctionComponent 的判斷, 沒有 defaulteProps, 不是構(gòu)造函數(shù), 簡(jiǎn)單函數(shù)組件只進(jìn)行淺比較
export function isSimpleFunctionComponent(type: any) {
return (
typeof type === 'function' &&
!shouldConstruct(type) &&
type.defaultProps === undefined
);
}
-
updateSimpleMemoComponent
更新渲染
- 優(yōu)先級(jí)低拼岳,進(jìn)行 PureComponent 功能的比較
- 有必要更新直接創(chuàng)建節(jié)點(diǎn),構(gòu)建 fiber 樹
沒有調(diào)和 reconcileChildren
reconcileChildren 也是把 nextChildren 結(jié)果的 ReactElement 生成 fiber 后賦值給 workInprogress.child 上不過多了很多 類型的判斷, memo 組件有必要更新是直接創(chuàng)建后 賦值在 workInprogress.child 上了况芒,memo 組件編寫只會(huì)返回常規(guī)的 ReactElement 組件內(nèi)容
completeUnitOfWork
- 根據(jù)是否中斷調(diào)用不同的處理方法
- 當(dāng)一側(cè)的子節(jié)點(diǎn)被 beginWork 更新組件完了執(zhí)行
- beginWork 完成各個(gè)組件的 update惜纸,然后返回他的 child
- 判斷是否有兄弟節(jié)點(diǎn)來執(zhí)行不同的操作
- 完成節(jié)點(diǎn)之后復(fù) effect 鏈
完成節(jié)點(diǎn)更新,重置 childExpirationTime
構(gòu)建 effect 鏈绝骚,供 commitRoot 提交階段使用
單側(cè)節(jié)點(diǎn)查找向上尋找節(jié)點(diǎn)
performUnitOfWork 遍歷 fiber 樹的順序
總結(jié)
- renderRoot 階段毕籽,通過 fiberRoot.current 構(gòu)建 nextUnitOfWork
- 在 workLoop 中對(duì) nextUnitOfWork 的每個(gè)節(jié)點(diǎn)進(jìn)行更新兽愤,從 fiberRoot 應(yīng)用的第一個(gè)子節(jié)點(diǎn)開始
- workLoop 中 while 循環(huán) 執(zhí)行 nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
- performUnitOfWork 中以 nextUnitOfWork.alternate 和 nextUnitOfWork 做兩個(gè) fiber 的對(duì)照,通過 beginWork 依次遍歷復(fù)用和創(chuàng)建 fiber 構(gòu)建成新的 nextUnitOfWork.child ,再返回 workLoop 中的 performUnitOfWork
- beginWork 根據(jù) tag 屬性判斷當(dāng)前 nextUnitOfWork 的節(jié)點(diǎn)類型與 alternate 對(duì)照來進(jìn)行對(duì)應(yīng)組件的復(fù)用更新胀屿,最后構(gòu)建成新的 fiber 樹膀藐,對(duì)節(jié)點(diǎn)上的操作進(jìn)行 effect 標(biāo)記
- 當(dāng) beginWork 遍歷完單側(cè)子樹后會(huì)通過 completeUnitOfWork 構(gòu)建 effect 更新鏈奔浅,方便 commit 提交階段更新
- completeUnitOfWork 在構(gòu)建完此側(cè)邊樹的 effect 鏈后, 向上尋找當(dāng)前 workInProgress 的 兄弟節(jié)點(diǎn)逛拱,繼續(xù) beginWork。
- completeUnitOfWork 中如果找不到 workInProgress 的兄弟節(jié)點(diǎn)就繼續(xù)找父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)滴须,直到找到 root 節(jié)點(diǎn)頂點(diǎn)返回 null舌狗,進(jìn)入 commitRoot 提交階段
- renderRoot 通過前后 fiberRoot.current 的對(duì)照逐層的復(fù)用更新,構(gòu)建出一個(gè)新的 fiber 樹扔水,標(biāo)記節(jié)點(diǎn) effect 等到提交階段操作