React源碼解析系列文章歡迎閱讀:
React16源碼解析(一)- 圖解Fiber架構
React16源碼解析(二)-建立更新
React16源碼解析(三)-ExpirationTime
React16源碼解析(四)-Scheduler
React16源碼解析(五)-更新流程渲染階段1
React16源碼解析(六)-更新流程渲染階段2
React16源碼解析(七)-更新流程渲染階段3
React16源碼解析(八)-更新流程提交階段
正在更新中...segmentfault
上篇文章講解到renderRoot & completeRoot。
React把組件的更新過程分爲了兩個階段:渲染階段(renderRoot)和提交階段(completeRoot)。
本文討論渲染階段。數組
從上篇文章末尾的renderRoot講起:架構
一、nextUnitOfWork = createWorkInProgress() 拷貝一份 fiber 節點,在 nextUnitOfWork 中修改,防止改變當前 fiberTree。nextUnitOfWork 是下一個要更新的節點。
二、進入workLoop。dom
// 開始渲染整顆樹,這個函數在異步模式下可能會被屢次執行,由於在異步模式下 // 能夠打斷任務。打斷也就意味着每次都得回到 root 再開始從上往下循環 function renderRoot( root: FiberRoot, isYieldy: boolean, isExpired: boolean, ): void { isWorking = true; ReactCurrentOwner.currentDispatcher = Dispatcher; const expirationTime = root.nextExpirationTimeToWorkOn; // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if ( expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null ) { // Reset the stack and start working from the root. resetStack(); nextRoot = root; nextRenderExpirationTime = expirationTime; // 獲取下一個須要工做的單元 nextUnitOfWork = createWorkInProgress( // FiberRoot 對應的 Rootfiber nextRoot.current, null, nextRenderExpirationTime, ); root.pendingCommitExpirationTime = NoWork; // ...... } // ...... do { try { // 循環更新節點 workLoop(isYieldy); } catch (thrownValue) { // 遇到某種錯誤 break; } while (true); // 這裏有一大堆的錯誤處理 // Ready to commit. onComplete(root, rootWorkInProgress, expirationTime); }
循環單元更新,對整顆 fiberTree 都遍歷一遍。異步
還記得以前傳入進來的isYieldy的麼,若是爲false,不可中斷,不斷的更新下一個節點任務(performUnitOfWork(nextUnitOfWork)),知道整棵樹更新完畢。若是能夠中斷,經過shouldYield()判斷當前幀是否還有時間更新,有時間就更新,沒有時間了就不更了。async
注:在workLoop 跟新後會有各類錯誤處理。ide
function workLoop(isYieldy) { // 對 nextUnitOfWork 循環進行判斷,直到沒有 nextUnitOfWork if (!isYieldy) { // 不可中斷 // Flush work without yielding while (nextUnitOfWork !== null) { // 一開始進來 nextUnitOfWork 是 root,每次執行 performUnitOfWork 後 // 都會生成下一個工做單元 nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } else { // 可中斷 // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } }
一、調用beginWork()更新當前任務節點
二、若是當前fiber樹已經更新到葉子節點了,則調用completeUnitOfWork更新。函數
// 開始組件更新 function performUnitOfWork(workInProgress: Fiber): Fiber | null { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here // means that we don't need an additional field on the work in // progress. // 得到 fiber 的替身,調和這一階段都是在替身上完成的 // 而後直接看 beginWork const current = workInProgress.alternate; // ...... let next; // ..... // 開始工做 next = beginWork(current, workInProgress, nextRenderExpirationTime); workInProgress.memoizedProps = workInProgress.pendingProps; // ...... // 當前fiber樹已經更新到葉子節點了 if (next === null) { // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; }
經過workInProgress.tag判斷當前是什麼類型的節點。
workInProgress.tag的全部類型定義在ReactWorkTags.js裏面oop
export const FunctionComponent = 0; //函數式組件 export const ClassComponent = 1; //class類組件 export const IndeterminateComponent = 2; // 並不肯定是函數組件仍是類組件 export const HostRoot = 3; // 組件樹根組件,能夠嵌套 export const HostPortal = 4; // 子樹. Could be an entry point to a different renderer. export const HostComponent = 5;// 標準組件,如地div, span等 export const HostText = 6;// 文本 export const Fragment = 7;// 片斷 export const Mode = 8; export const ContextConsumer = 9; export const ContextProvider = 10; export const ForwardRef = 11; export const Profiler = 12; export const SuspenseComponent = 13; export const MemoComponent = 14; export const SimpleMemoComponent = 15; export const LazyComponent = 16; export const IncompleteClassComponent = 17;
接下來我會依次講解下面這些組件的更新:this
一、FunctionComponent(函數組件),return updateFunctionComponent(…)。
二、ClassComponent(class類組件) return updateClassComponent(…)
三、IndeterminateComponent(不肯定是函數組件仍是類組件),return mountIndeterminateComponent(…)
四、HostRoot(組件樹根組件),return updateHostRoot(…);
五、HostComponent(標準dom節點),return updateHostComponent(…);
六、HostText(文本),updateHostText(…);
七、HostPortal,updatePortalComponent(…)
八、ForwardRef,updateForwardRef(…)
九、Mode,updateMode(…)
十、MemoComponent,updateMemoComponent(….)
function beginWork( current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime, ): Fiber | null { const updateExpirationTime = workInProgress.expirationTime; if (current !== null) { const oldProps = current.memoizedProps; const newProps = workInProgress.pendingProps; // 判斷 props 和 context 是否改變 // 判斷當前 fiber 的優先級是否小於本次渲染的優先級,小於的話能夠跳過 if ( oldProps === newProps && !hasLegacyContextChanged() && (updateExpirationTime === NoWork || updateExpirationTime > renderExpirationTime) ) { // ...... } // bailoutOnAlreadyFinishedWork 會判斷這個 fiber 的子樹是否須要更新,若是有須要更新會 clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 不然爲 null return bailoutOnAlreadyFinishedWork( current, workInProgress, renderExpirationTime, ); } } // Before entering the begin phase, clear the expiration time. // 進行更新先把當前 fiber 的 expirationTime 設置爲 NoWork workInProgress.expirationTime = NoWork; // 根據 fiber 的 tag 類型進行更新 switch (workInProgress.tag) { case IndeterminateComponent: ...... case LazyComponent: ...... case FunctionComponent: ...... case ClassComponent: ...... case HostRoot:...... case HostComponent:...... case HostText:...... case SuspenseComponent:...... case HostPortal:...... case ForwardRef: ...... case Fragment:...... case Mode:...... case Profiler:...... case ContextProvider:...... case ContextConsumer:...... case MemoComponent: ...... case SimpleMemoComponent: ...... case IncompleteClassComponent:...... default: invariant( false, 'Unknown unit of work tag. This error is likely caused by a bug in ' + 'React. Please file an issue.', ); } }
下篇文章繼續講解這些組件的更新。
文章若有不妥,歡迎指正~