网站排名优化平台,网站建设个人网银,苏州网站建设开发,网站背景大小摘要
在上一篇中#xff0c;实现了多节点的渲染。但是之前写得diff算法#xff0c;只能适用于单节点的情况#xff0c;例如这种情况#xff1a;
divpspan/span/p
/div如果对于多节点的情况#xff1a;
ul…摘要
在上一篇中实现了多节点的渲染。但是之前写得diff算法只能适用于单节点的情况例如这种情况
divpspan/span/p
/div如果对于多节点的情况
ulli/lili/lili/li
/ul之前实现的diff算法就不会有效果了所以在这一篇中我们主要实现针对于多节点的diff算法。
实现之前我们先将index.js修改一下
function App() {const [num, setNum] useState(100)const click1 () {console.log(num);setNum(num 1)}return num % 2 0 ? jsx(ul, {onClick: click1,key: ul,children: [jsx(li, {children: 1,key: 1}), jsx(li, {children: 2,key: 2}), jsx(li, {children: 3,key: 3})]}): jsx(ul, {onClick: click1,key: ul,children: [jsx(li, {children: 2,key: 2}), jsx(li, {children: 1,key: 1}), jsx(li, {children: 3,key: 3})]});
}ReactDOM.createRoot(root).render(App /)1.修改beginWork流程
在reconcileChildren方法里面我们判断了如果element为数组的情况就是多节点。所以我们需要在这里进行diff算法的处理。
function reconcileChildren(parent,element) {//其他代码。。。。}else if(Array.isArray(element) element.length 0) {const newChild diffReconcileManyChildren(parent, element);if(newChild) {return newChild}//其他代码。。。。所以我们的diff算法那主要是在diffReconcileManyChildren方法里面实现。
对于多节点的Diff我们需要进行以下步骤。
创建变量lastIndex用来标记索引将旧的filberNode列表转换为map结构key为filberNode的keyvalue为filberNode遍历新的element数组。如果element.key可以在map中找到lastIndex记录为找到的filberNode的index如果找不到创建新的FilberNode继续遍历如果又在map中找到filberNode比较fiberNode的index和lastIndex.如果index lastIndex给filberNode打上移动的标志
基于上面的步骤实现diffReconcileManyChildren方法
function diffReconcileManyChildren(filberNode, element) {let firstChild filberNode.child;if(!firstChild) {return;}const head {sibling: null};const oldChildren []while(firstChild) {oldChildren.push(firstChild);firstChild firstChild.sibling;}const oldMap new Map();oldChildren.forEach((item,index) {item.index indexif(item.key) {oldMap.set(item.key, item)}else{oldMap.set(index, item)}})let lastIndex 0;let empty headfor(let i0; ielement.length; i) {if(!element[i].key){continue;}const useFilber oldMap.get(element[i].key);useFilber.sibling null;if(useFilber) {if(useFilber.index lastIndex) {useFilber.flags insert}useFilber.memoizedProps element[i]lastIndex useFilber.index;empty.sibling useFilber;empty empty.sibling;oldMap.delete(element[i].key)}else{const filberNode new FilberNode(HostComponent, element[i].props, element[i].key) filberNode.type element[i].typeempty.sibling filberNode;empty empty.sibling;}}return head.sibling;
}经过上面的处理beginWork流程结束可复用的filberNode就不会重复创建。
2.修改completeWork流程
在beginWork中可复用的节点已经被打上了insert的标志所以在updateCompleteHsotComponent中我们要判断是不是insert的标志如果是就不能无脑创建而是通过移动DOM的位置来复用DOM。
同时也要对同级的sibling进行递归处理。
function updateCompleteHostComponent(filberNode) {//其他代码。。。。if(element.key filberNode.key element.type filberNode.type) {addPropsToDOM(filberNode.stateNode, filberNode.pendingProps);if(filberNode.flags insert) {const parent filberNode.return;parent.stateNode.insertBefore(filberNode.stateNode, filberNode.sibling?.stateNode)}//其他代码if(filberNode.sibling) {completeWork(filberNode.sibling)}
}在对HostText的处理中也要考虑当前的操作是更新还是替换。
function completeHostText(filberNode) {//其他代码。。。。。if(parent parent.stateNode parent.tag HostComponent) {if(!parent.stateNode) {parent.stateNode.appendChild(element);}else{parent.stateNode.replaceChildren(element);}}//其他代码。。。。
}