微网站自助建站后台,如何制作公众号教程,专业的企业进销存软件厂家价格,做视频周边的网站摘要
经过之前的几篇文章#xff0c;我们已经实现了一个可以进行更新渲染的假React。但是如果我们把我们的jsx修改成这样#xff1a;
function App() {const [age, setAge] useState(20)const click function() {setAge(age 1)}return age % 2 0 ? jsx(div我们已经实现了一个可以进行更新渲染的假React。但是如果我们把我们的jsx修改成这样
function App() {const [age, setAge] useState(20)const click function() {setAge(age 1)}return age % 2 0 ? jsx(div, {key: div1,children: jsx(span, {key: span,children: div1,onClick: click})}) : jsx(div, {key: div1,children: jsx(span, {key: span,children: div2,onClick: click})})
}ReactDOM.createRoot(root).render(App /)虽然页面的效果是正确的。但对于两个jsx二者的区别仅仅是span标签里面的内容不同。 但是在程序里面我们是相当于每次都重新beginWork重新的创建Filber树重新的创建真实DOM。 而对于这里div和span标签它没有任何的改变我们是否可以用一种优化策略从而对旧资源进行利用呢 所以Diff由此而来。
这一篇先只说单节点的Diff因为目前还没实现带有sibling的情况。
1.修改beginWork
我们回顾一下在beginWork里面干了什么。在将jsx转换为ReactElement后我们会通过beginWork来构建一颗Filber树。 那如果对于可复用的FilberNode我们是否可以不去创建直接复用呢 可以的那对于React来说什么节点是可复用的呢
如果旧的FilberNode和新的ReactElement key相同type相同。 那么就是可以复用的。
所以在beginWork中的reconcileChildren方法里如果我们发现上面的情况我们就不会创建新的FilberNode。
function reconcileChildren(parent,element) {const newChild diffReconcileChildren(parent, element);if(newChild) {return newChild}//其他代码。。。return filberNode
}2.实现diffReconcileChildren方法
该方法接受两个参数第一个是父节点第二个是新的ReactElement。
1我们要先拿到父节点的child比较child和element的key和type。 2将element保存在child的memoizedState里面。 3然后其他逻辑和reconcileChildren里的一样即可。 4直接返回child。
function diffReconcileChildren(filberNode, element) {const child filberNode.child;if(child child.key child.type) {if(child.key element.key child.type child.type) {child.memoizedProps element;child.pendingProps element.props;if(child.tag HostText) {child.pendingProps element}return child;}}
}这里为什么要将element保存在memoizedState里面是因为虽然节点没有改变但是子节点可能有改变或者属性会有改变。所以要在后面的completeWork里进行处理。
3.修改completeWork
经过上面的处理后FilberNode不会无脑的重复创建而是复用了。而completeWork的工作主要是创建真实DOM挂载在FilberNode的stateNode上。
所以在completeWork中也不能无脑的创建真实DOM而是也要判断是否是可复用的。
export const completeWork (filberNode) {const tag filberNode.tagswitch (tag) {case HostComponent: {if(filberNode.stateNode ! null){//更新updateCompleteHostComponent(filberNode)}else{completeHostComponent(filberNode)}break;}//其他代码。。。。}
}所以在对HostComponent的处理上如果发现不是mount阶段就要判断是否需要复用旧的。
4.实现updateCompleteHostComponent方法
在该方法中我们接受filberNode。同时我们可以拿到它的memoizedState在beginWork中传过来的。
再判断一下key和type如果依旧相同那么说明是可复用的。我们直接不创建新的DOM即可。
function updateCompleteHostComponent(filberNode) {const element filberNode.memoizedProps;if(element.key filberNode.key element.type filberNode.type) {addPropsToDOM(filberNode.stateNode, filberNode.pendingProps)}else{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)
}addPropsToDOM方法是我们在实现事件机制的时候需要调用的方法。
OK这样我们就实现了单节点的Diff算法。