1。什么是差异算法?传统Diff:diff算法是差分搜索算法;对于HtmlDOM结构,就是树差搜索算法;而对于计算两棵树的时间差,复杂度为O(n^3),显然代价太高,React不可能采用这种传统算法;ReactDiff:前面提到过,React使用虚拟DOM技术实现到真实DOM的映射,即ReactDiff算法的差异查找,本质是寻找两个JavaScript对象之间的差异;基于三种策略:WebUI中DOM节点的跨层移动是极其罕见的,可以忽略不计。(treediff)类相同的两个组件会生成相似的树结构,类不同的两个组件会生成不同的树结构(componentdiff)对于同一层级的一组子节点,它们可以通过唯一id来区分.(elementdiff)2.ReactDiff算法的解释需要先搞清楚。Diff算法只会在React更新阶段使用;React更新机制:ReactDiff算法优化策略图:React更新阶段会对ReactElement类型进行不同的判断操作;ReactElement类型包括三种类型:文本、Dom和组件;各类元素的更新处理方式:自定义元素的更新主要是更新渲染节点,把店主交过来的渲染节点对应的组件去管理更新。文本节点的更新很简单,直接更新副本即可。浏览器基本元素的更新分为两部分:更新属性,比较前后属性的差异,部分更新。并处理特殊属性,例如事件绑定。子节点的更新,子节点的更新主要是找出差异对象。在查找差异对象时,也会使用上面的shouldUpdateReactComponent进行判断。如果可以直接更新,则递归调用子节点的更新,子节点也会递归求差。目的。不能直接更新删除以前的对象或添加新对象。然后根据不同对象操作dom元素(位置变化、删除、添加等)。实际上,Diff算法只是在React更新阶段的DOM元素更新过程中被调用;你为什么这么说?1.如果是更新文本类型,如果内容不同,直接更新替换,不会调用复杂的Diff算法:ReactDOMTextComponent.prototype.receiveComponent(nextText,transaction){//与以前保存的字符串if(nextText!==this._currentElement){this._currentElement=nextText;varnextStringText=''+nextText;if(nextStringText!==this._stringText){this._stringText=nextStringText;varcommentNodes=this.getHostNode();//替换文本元素DOMChildrenOperations.replaceDelimitedText(commentNodes[0],commentNodes[1],nextStringText);}}}2.对于自定义组件元素:classTabextendsComponent{constructor(props){super(props);this.state={index:1,}}shouldComponentUpdate(){....}render(){return(
)}}需要明确的是什么是组件,可以说组件只是一个Html结构的包装容器,具有管理这个Html结构状态的能力;比如上面的Tab组件:它的本质就是render函数Structure返回的Html,而我们说的Tab类就是这个Html结构的包装容器(可以理解为包装盒);在React渲染机制图中可以看到,自定义组件最终结合了ReactDiff优化策略1(不同的类两个组件结构不同)3.基本元素:ReactDOMComponent.prototype.receiveComponent=function(nextElement,transaction,context){varprevElement=this._currentElement;this._currentElement=nextElement;this.updateComponent(transaction,prevElement,nextElement,context);}ReactDOMComponent.prototype.updateComponent=function(transaction,prevElement,nextElement,context){//需要一个单独的更新属性this._updateDOMProperties(lastProps,nextProps,transaction,isCustomComponentTag);//更新子节点this._updateDOMChildren(lastProps,nextProps,transaction,context);//......}diff算法在this._updateDOMChildren方法内部被调用3.React中Diff算法的实现_updateChildren:function(nextNestedChildrenElements,transaction,context){varprevChildren=this._renderedChildren;varremovedNodes={};varmountImages=[];//获取新的子元素数组varnextChildren=this._reconcilerUpdateChildren(prevChildren,nextNestedChildrenElements,mountImages,removedNodes,transaction,context);如果(!nextChildren&&!prevChildren){返回;}变种更新=空;变量名称;varnextIndex=0;extvarlastIndex=0;变量0;varlastPlacedNode=null;for(nameinnextChildren){if(!nextChildren.hasOwnProperty(name)){继续;}varprevChild=prevChildren&&prevChildren[名称];varnextChild=nextChildren[名字];if(prevChild===nextChild){//同一个引用表示使用了同一个组件,所以需要做移动操作//移动已有的子节点//注意:这里是根据判断是否移动到nextIndex,lastIndexupdates=enqueue(更新,this.moveChild(prevChild,lastPlacedNode,nextIndex,lastIndex));//更新lastIndexlastIndex=Math.max(prevChild._mountIndex,lastIndex);//更新组件的.mountIndex属性prevChild._mountIndex=nextIndex;}else{if(prevChild){//更新lastIndexlastIndex=Math.max(prevChild._mountIndex,lastIndex);}//在指定位置添加一个新的子节点updates=enqueue(updates,this._mountChildAtIndex(nextChild,mountImages[nextMountIndex],lastPlacedNode,nextIndex,transaction,context));nextMountIndex++;}//更新nextIndexnextIndex++;lastPlacedNode=ReactReconciler.getHostNode(nextChild);具有不同节点的旧子节点和新子节点for(nameinremovedNodes){if(removedNodes.hasOwnProperty(name)){updates=enqueue(updates,this._unmountChild(prevChildren[name],removedNodes[name]));}}}5.基于mediumDiff的开发建议基于treediff:开发组件时,注意保持DOM结构的稳定性;即,尽量少动态操作DOM结构,尤其是移动操作时节点数量过多或页面更新次数过多时,页面卡顿现象会更加明显。此时您可以通过CSS隐藏或显示节点,而不是实际删除或添加DOM节点。基于组件diff:注意使用shouldComponentUpdate()来减少组件不必要的更新。相似的结构尽量封装到组件中,这样既减少了代码量,又减少了组件diff的性能消耗。基于元素差异:对于列表结构,尽量减少将最后一个节点移动到列表头部的操作。当节点数量过多或者更新操作过于频繁时,都会在一定程度上影响React的渲染性能。接下来会手动实现一个简单的Diff算法,很快就会更新,敬请期待~~~《积步数,行万里路》——持续更新~,喜欢就留个赞吧!往期经典好文:团队协作必备的Git操作浅谈Js前端标准化从React渲染过程分析Diff算法相关专栏推荐:React学习之路