目前,我们已经实现了单节点的,beginWork,completeWork,diff流程。但是对于多节点的情况,比如:
<div>
<span></span>
<span></span>
</div>
这种情况,我们还没有处理,而这种JSX会被,转换为:
jsxs("div", {
children: [jsx("span", {}),jsx("span", {})]
});
之前的children就直接是一个对象jsx,因为是单节点。而现在,是通过数组的方式表示。
而这一篇,主要就是对多节点的情况进行处理,所以我们要修改一下我们的index.js文件:
function App() {
const [text, setText] = useState('100')
const click1 = () => {
setText(text + 1)
}
return jsx("div", {
children: [jsx("div", {
children: text,
onClick: click1
}), jsx("div", {
children: ["text1","text2",jsx("span", {
children: "span"
})]
})]
})
}
ReactDOM.createRoot(root).render(<App />)
再次回顾,beginWork流程,主要是通过ReactElement,进行创建Filber树。而之前我们只考虑了return和child的情况,并没有将sibling考虑进去。
现在我们要将sibling这个属性,加进去,让整个Filber树更加全面,所以要修改我们的reconcileChidren方法。
之前在这个方法里面,我们判断element类型的时候,有FunctionComponent,HostComponent和HostText。现在因为有了多节点,所以element也有可能是数组。
如果是数组,我们就将第一个节点继续给parent.child。剩下的节点用sibling连接起来。
这里所有的sibling的return依旧指向parent。
function reconcileChildren(parent,element) {
//其他代码。。。
}else if(Array.isArray(element) && element.length > 0) {
let child = reconcileChildren(parent, element.shift());
let head = child;
while(element.length > 0) {
let sibling = reconcileChildren(parent, element.shift());
sibling.return = parent
child.sibling = sibling;
child = child.sibling;
}
return head;
}
//其他代码。。。
}
那再updateHostComponent中,beginWork的递归,就不能只递归child了。sibling也要递归一下:
function updateHostComponent(filberNode) {
const nextChildren = filberNode.pendingProps.children;
const newFilberNode = reconcileChildren(filberNode,nextChildren);
filberNode.child = newFilberNode;
newFilberNode.return = filberNode;
beginWork(newFilberNode);
if(newFilberNode.sibling) {
beginWork(newFilberNode.sibling)
}
}
还有一个问题就是,对于HostText类型的节点,因为不可能有child,所以在之前的递归流程中,并没有进行处理。
但是有了sibling之后,对于HostText类型的,也要对它的sibling进行递归。
function updateHostText(filberNode) {
if(filberNode.sibling) {
beginWork(filberNode.sibling)
}
}
export const beginWork = (nowFilberNode) => {
switch (nowFilberNode.tag) {
//其他代码。。。
case HostText: {
return updateHostText(nowFilberNode)
}
case FunctionComponent: {
}
default: {
console.error('错误的类型')
}
}
}
在completeWork中,我们主要就是构建离屏DOM树,然后挂载在stateNode上。对于该流程来说,我们只需要在递归的过程中,将sibling属性考虑上即可:
对于HostComponent类型:
function completeHostComponent(filberNode) {
const type = filberNode.type;
const element = document.createElement(type);
addPropsToDOM(element, filberNode.pendingProps)
filberNode.stateNode = element;
const parent = filberNode.return;
if(parent && parent.stateNode && parent.tag === HostComponent) {
parent.stateNode.appendChild(element)
}
completeWork(filberNode.child);
if(filberNode.sibling) {
completeWork(filberNode.sibling)
}
}
对于HostText类型:
function completeHostText(filberNode) {
const content = filberNode.pendingProps;
const element = document.createTextNode(content)
filberNode.stateNode = element
const parent = filberNode.return;
if(parent && parent.stateNode && parent.tag === HostComponent) {
parent.stateNode.appendChild(element)
}
if(filberNode.sibling) {
completeWork(filberNode.sibling)
}
}
这样,多节点的mount渲染,我们就已经处理完了。