摘要
經過之前的幾篇文章,我們實現了基本的jsx,在頁面渲染的過程。但是如果是通過函數組件寫出來的組件,還是不能渲染到頁面上的。
所以這一篇,主要是對之前寫得方法進行修改,從而能夠顯示函數組件,所以現在我們在index.js文件中,修改一下jsx的寫法。修改成函數組件:
import jsx from '../src/react/jsx.js'
import ReactDOM from '../src/react-dom/index'const root = document.querySelector('#root');function App() {return jsx("div", {ref: "123",children: jsx("span", {children: "456"})});
}ReactDOM.createRoot(root).render(<App />)
這里因為需要使用我們自己的jsx方法。所以在App里面返回的依舊是通過之前的方式進行調用。
1.修改reconcileChildren方法
我們來回憶一下,在beginWork階段,我們主要是通過ReactElement,創建FilberNode。而reconcileChildren,就是創建FilberNode的方法。
在之前我們只處理了HostText類型和HostComponent類型,所以在這個方法里面,我們要對函數類型進行兼容,而作為函數組件的ReactElment,它最顯而易見的特點就是type的值是一個函數。
例如上面的App組件,對應的ReactElement的type就是App。所以我們可以通過type來判斷組件的類型:
function reconcileChildren(element) {let tag;if(typeof element.type === 'function') {tag = FunctionComponent}//其他代碼console.log(filberNode)return filberNode
}
我們打印一下看看,這個函數組件是否滿足預期:
2.updateFunctionComponent方法
現在有了tag為FunctionComponent類型的FilberNode,在beginWork里面,我們就要對這個類型的FilberNode進行處理:
function beginWork(nowFilberNode) {switch (nowFilberNode.tag) {//其他代碼case FunctionComponent: {return updateFunctionComponent(nowFilberNode)}//其他代碼}
}
現在我們來實現updateFunctionComponent方法。
之前對于HostComponent類型的FilberNode,它的子節點其實就是它對應的ReactElement。
但是對于函數類型的FilberNode,我們想一下不就是它自己的返回值嘛?所以我們直接調用這個函數就能拿到它的子FilberNode了。
function updateFunctionComponent(filberNode) {const nextChildren = filberNode.type();const newFilberNode = reconcileChildren(nextChildren);filberNode.child = newFilberNode;newFilberNode.return = filberNode;beginWork(newFilberNode)
}
2.修改completeWork方法
對于completeWork方法, 它的主要作用(目前)是給對應的FilberNode增加stateNode,而函數組件并沒有自己對應的StateNode,所以直接繼續遞歸就可以了:
export const completeWork = (filberNode) => {const tag = filberNode.tagswitch (tag) {//其他代碼。。。case FunctionComponent: {completeWork(filberNode.child)}}
}
3.修改commitWork方法
對于之前的commitWork,我們是直接將最外層的FilberNode的stateNode掛載了容器上,現在由于最外層的可能是FunctionComponent,它是沒有自己的stateNode的。所以我們要找到具有stateNode的最外層FilberNode。
import { HostComponent } from "./filberNode";export function commitWork(filberRootNode) {const container = filberRootNode.container;let node = filberRootNode.finishedWork;while( node.tag !== HostComponent ){node = node.child}container.appendChild(node.stateNode)
}
OK,經過上面的修改,我們的App組件也可以正常渲染了。