摘要
經過上一章,我們得到的FilberNode已經具有了child和return屬性。一顆Filber樹的結構已經展現出來了。
那我們最終是想在頁面渲染真實的DOM。所以我們現在要在completeWork里,構建出一顆離屏的DOM樹。
之前在說FilberNode的屬性時,我們提到過一個屬性stateNode。它就是用來保存每個FilberNode的真實DOM。
OK,現在我們開干,準備實現completeWork。
1.completeWork方法
在completeWork方法里,我們將剛才準備好的FilberNode(最外層的)傳進來。
completeWork方法也一定是一個遞歸的過程,每次調用的時候我們要判斷當前的FilberNode的tag類型。
在判斷當前的FilberNode是否具有stateNode,如果有,就說明不是第一次更新。如果沒有,就說明此時是mount第一次渲染的階段。
export const completeWork = (filberNode) => {console.log(filberNode);const tag = filberNode.tagswitch (tag) {case HostComponent: {if(filberNode.stateNode !== null){//更新}else{completeHostComponent(filberNode)}break;}case HostText: {break;}case HostRoot: {}}
}
2.創建真實DOM
拿到每個FilberNode后,如果他是HostComponent類型的。我們實現出completeWork方法。
首先我們可以通過拿到FilberNode的type(div,span),知道真實DOM的類型,通過document的方法創建出對應的element。
然后再通過return屬性,拿到當前節點的父節點。并且在父節點的stateNode中,添加創建好的element。
function completeHostComponent(filberNode) {const type = filberNode.type;const element = document.createElement(type);filberNode.stateNode = element;const parent = filberNode.return;if(parent && parent.stateNode && parent.tag === HostComponent) {parent.stateNode.appendChild(element)}completeWork(filberNode.child)
}
對于HostText類型,我們可以直接創建文本節點,然后掛載在stateNode上面即可:
function completeHostText(filberNode) {const content = filberNode.pendingProps;const element = document.createTextNode(content)filberNode.stateNode = elementconst parent = filberNode.return;if(parent && parent.stateNode && parent.tag === HostComponent) {parent.stateNode.appendChild(element)}
}
這里值得注意的是,在創建真實DOM的時候,這里并沒有處理props相關的內容。只創建了對應的DOM結構。
3.掛載finishedWork
最后我們將執行完beginWork和completeWork的FilberRootNode。掛載在hostRootFilber上面,
function updateContainer(root, element) {const hostRootFilber = root.current;const update = createUpdate(element);hostRootFilber.updateQueue = createUpdateQueue()enqueueUpdate(hostRootFilber.updateQueue, update);beginWork(hostRootFilber);completeWork(hostRootFilber);root.finishedWork = hostRootFilber;
}
4.問題
這里我們打印一下root,可以看到:
root的current和finishedWork其實是一模一樣的,但React其實并非是這樣處理的。經過beginWork和completeWork處理的節點,并不是最外層的FilberNode,而是它的alternate。
所以我們調用beginWork和completeWork處理的應該是FilberNode的alternate。
5.效果
為了看到效果我們可以寫一個方法,當然這個方法里面內容不可能這沒少。但是我們可以先實現一下:
export function commitWork(filberRootNode) {const container = filberRootNode.container;const child = filberRootNode.finishedWork.child.stateNode;container.appendChild(child)
}
然后再beginWork和completeWork執行完后,執行這個方法。
就可以看到頁面正產渲染節點了。