您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“React18系列commit怎么實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“React18系列commit怎么實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
本系列是講述從0開始實現一個react18的基本版本。由于React
源碼通過Mono-repo 管理倉庫,我們也是用pnpm
提供的workspaces
來管理我們的代碼倉庫,打包我們使用rollup
進行打包。
React
中commit分為下面三個階段,這節我們講解簡單的commit階段:
beforeMutation階段
mutation階段
layout階段
這節主要講解commit其中的mutation placement
階段:
我們經常把Renderer
工作的階段被稱為commit
階段。在commit
階段,會將我們上一講中生成的各種flags提交(commit
)到宿主環境UI中。我們前端通常使用的是ReactDOM
進行處理。這節我們簡單的實現了一個ReactDom
包,看看是如何把調和和瀏覽器環境連起來的。
在React
中,react-reconciler
和react-dom
是2個不同的包,react-dom
主要是提供瀏覽器宿主相關的方法。回想我們每次在使用react
開發項目的過程中,通過下方代碼調用:
ReactDom.createRoot(root).render(<App />)
所以需要提供createRoot
方法。返回一個render
方法,接收ReactElement
元素。
export function createRoot(container: Container) { const root = createContainer(container); return { render(element: ReactElementType) { updateContainer(element, root); }, }; }
內部通過createContainer
和updateContainer
建立2個包的連接。具體的流程可以查看我們之前的章節。
在上一節中,我們在workLoop
執行完后后得到了一個帶有標記的wip fiber tree
(如下圖所示), 在commit階段,我們要通過這個fiber樹將內容渲染到屏幕中。
將最后生成的finishedWork
傳遞給commitRoot
, 然后根據頂部的flags
和subtreeFlags
來判斷是否有渲染節點:
function commitRoot(root: FiberRootNode) { const finishedWork = root.finishedWork; if (finishedWork === null) { return; } // 重置 root.finishedWork = null; // 判斷是否存在子階段需要執行的操作 const subtreeHasEffect = (finishedWork.subtreeFlags & MutationMask) !== NoFlags; // 子節點是否有更新 const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags; // 根節點是否更新 if (subtreeHasEffect || rootHasEffect) { // beforeMutation // mutation Placement commitMutationEffects(finishedWork); root.current = finishedWork; // layout } else { root.current = finishedWork; } }
我們知道div對應的fiberNode
是標有flags = placement
的,所以在hostFiberNode
中的subtreeHasEffect
是有值的,所以會走到commitMutationEffects
分支。那它內部到底是有什么作用呢?
commitMutationEffects執行
的主要作用就是找到對應flags的fiberNode, 并執行相應的Dom操作。
我們需要向下遍歷找到最底部的subtreeFlags
不為0的fiberNode的子fiberNode
節點
由于父節點的subtreeFlags
存在,不代表child
對應的flags
存在,可能是child
對應的sibling
節點的flags
存在
所以在找到對應的subtreeFlags
的child
的fiberNode后,需要在向上遍歷,查找對應的sibling
節點。
對每一個fiberNode
我們會執行commit Effects的操作。
const commitMutationEffectsOnFibers = (finishedWork: FiberNode) => { const flags = finishedWork.flags; if ((flags & Placement) !== NoFlags) { commitPlacement(finishedWork); finishedWork.flags &= ~Placement; } // flags update // flags childDeletion }; const commitPlacement = (finishWork: FiberNode) => { if (__DEV__) { console.warn("執行commitPlacement操作", finishWork); } // parentDom 插入 finishWork對應的dom // 1. 找到parentDom const hostParent = getHostParent(finishWork); if (hostParent !== null) { appendPlacementNodeIntoContainer(finishWork, hostParent); } };
其中有一個getHostParent
是獲取到對應父Container
(容器元素),對應瀏覽器環境就是Dom
,
找父contianer容器,向上遞歸 主要是分為2大類。第一類是對應HostComponent
(類似<div>
), 第二類就是根節點(#root
), 他們分別對應不同的位置。
function getHostParent(fiber: FiberNode): Container | null { let parent = fiber.return; while (parent) { const parentTag = parent.tag; // HostComponent HostRoot if (parentTag === HostComponent) { return parent.stateNode as Container; } if (parentTag === HostRoot) { return (parent.stateNode as FiberRootNode).container; } parent = parent.return; } if (__DEV__) { console.warn("未找到HostParent"); } return null; }
當我們上一步找到父container后,接下來就是要把自身的stateNode
插入到父container中,然后渲染的界面上。
appendPlacementNodeIntoContainer
這個函數接受2個參數,第一個是當前fiberNode
, 第二個父container。想想什么樣的fiberNode
需要渲染到屏幕中。目前demo層級來看,只有下面2個節點需要展示到屏幕中。
HostComponent
HostText
所以appendPlacementNodeIntoContainer
需要向下找到對應的節點。然后插入到contaienr中
function appendPlacementNodeIntoContainer( finishedWork: FiberNode, hostParent: Container ) { // fiber Host if (finishedWork.tag === HostComponent || finishedWork.tag === HostText) { appendChildToContainer(hostParent, finishedWork.stateNode); return; } const child = finishedWork.child; if (child !== null) { appendPlacementNodeIntoContainer(child, hostParent); let sibling = child.sibling; while (sibling !== null) { appendPlacementNodeIntoContainer(sibling, hostParent); sibling = sibling.sibling; } } return null; }
我們通過一個特定的例子,來講解commit mutation
中具體的執行過程。比如:我們有如下的結構,更新的flags
標記如下:
commitMutationEffects
會找到subtreeFlags
值不為0的頂層fiberNode
,然后開始向下遍歷,直到找到world fiberNode
遞歸向上的執行commitMutationEffectsOnFibers
,先自己,然后sibling
, 然后return
。
commitMutationEffectsOnFibers
: 例如world fiberNode
,自身flags = 1
,然后會通過getHostParent
找到類型為HostComponent
的div fiberNode
, 然后把自己的stateNode
對應的dom, 通過appendChild
插入到div fiberNode
的stateNode
中。
這樣一直向上,最終就會把div fiberNode
對應的dom元素,append
到root
中。
讀到這里,這篇“React18系列commit怎么實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。