您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

React中Diff算法是什么?Diff算法的策略及实现

2024/3/14 17:49:46发布20次查看
本篇文章给大家带来的内容是关于react中diff算法是什么?diff算法的策略及实现,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
1、什么是diff算法传统diff:diff算法即差异查找算法;对于html dom结构即为tree的差异查找算法;而对于计算两颗树的差异时间复杂度为o(n^3),显然成本太高,react不可能采用这种传统算法;
react diff:
之前说过,react采用虚拟dom技术实现对真实dom的映射,即react diff算法的差异查找实质是对两个javascript对象的差异查找;
基于三个策略:
web ui 中 dom 节点跨层级的移动操作特别少,可以忽略不计。(tree diff)
拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结(component diff)
对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。(element diff)
2、react diff算法解读首先需要明确,只有在react更新阶段才会有diff算法的运用;
react更新机制:
react diff算法优化策略图:
react更新阶段会对reactelement类型判断而进行不同的操作;reactelement类型包含三种即:文本、dom、组件;
每个类型的元素更新处理方式:
自定义元素的更新,主要是更新render出的节点,做甩手掌柜交给render出的节点的对应component去管理更新。
text节点的更新很简单,直接更新文案。
浏览器基本元素的更新,分为两块:
更新属性,对比出前后属性的不同,局部更新。并且处理特殊属性,比如事件绑定。
子节点的更新,子节点更新主要是找出差异对象,找差异对象的时候也会使用上面的shouldupdatereactcomponent来判断,如果是可以直接更新的就会递归调用子节点的更新,这样也会递归查找差异对象。不可直接更新的删除之前的对象或添加新的对象。之后根据差异对象操作dom元素(位置变动,删除,添加等)。
事实上diff算法只被调用于react更新阶段的dom元素更新过程;为什么这么说?
1、 如果为更新文本类型,内容不同就直接更新替换,并不会调用复杂的diff算法:
 reactdomtextcomponent.prototype.receivecomponent(nexttext, transaction) {    //与之前保存的字符串比较    if (nexttext !== this._currentelement) {      this._currentelement = nexttext;      var nextstringtext = '' + nexttext;      if (nextstringtext !== this._stringtext) {        this._stringtext = nextstringtext;        var commentnodes = this.gethostnode();        // 替换文本元素        domchildrenoperations.replacedelimitedtext(          commentnodes[0],          commentnodes[1],          nextstringtext        );      }    }  }
2、对于自定义组件元素:
class tab extends component {    constructor(props) {        super(props);        this.state = {            index: 1,        }    }    shouldcomponentupdate() {        ....    }    render() {        return (            <p>                <p>item1</p>                <p>item1</p>            </p>        )    }    }
需要明确的是,何为组件,可以说组件只不过是一段html结构的包装容器,并且具备管理这段html结构的状态等能力;
如上述tab组件:它的实质内容就是render函数返回的html结构,而我们所说的tab类就是这段html结构的包装容器(可以理解为一个包装盒子);
在react渲染机制图中可以看到,自定义组件的最后结合react diff优化策略一(不同类的两个组件具备不同的结构)
3、基本元素:
reactdomcomponent.prototype.receivecomponent = function(nextelement, transaction, context) {    var prevelement = 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    );    // ......}
在this._updatedomchildren方法内部才调用了diff算法。
3、react中diff算法的实现_updatechildren: function(nextnestedchildrenelements, transaction, context) {    var prevchildren = this._renderedchildren;    var removednodes = {};    var mountimages = [];    // 获取新的子元素数组    var nextchildren = this._reconcilerupdatechildren(      prevchildren,      nextnestedchildrenelements,      mountimages,      removednodes,      transaction,      context    );    if (!nextchildren && !prevchildren) {      return;    }    var updates = null;    var name;    var nextindex = 0;    var lastindex = 0;    var nextmountindex = 0;    var lastplacednode = null;    for (name in nextchildren) {      if (!nextchildren.hasownproperty(name)) {        continue;      }      var prevchild = prevchildren && prevchildren[name];      var nextchild = nextchildren[name];      if (prevchild === nextchild) {        // 同一个引用,说明是使用的同一个component,所以我们需要做移动的操作        // 移动已有的子节点        // notice:这里根据nextindex, lastindex决定是否移动        updates = enqueue(          updates,          this.movechild(prevchild, lastplacednode, nextindex, lastindex)        );        // 更新lastindex        lastindex = math.max(prevchild._mountindex, lastindex);        // 更新component的.mountindex属性        prevchild._mountindex = nextindex;      } else {        if (prevchild) {          // 更新lastindex          lastindex = math.max(prevchild._mountindex, lastindex);        }        // 添加新的子节点在指定的位置上        updates = enqueue(          updates,          this._mountchildatindex(            nextchild,            mountimages[nextmountindex],            lastplacednode,            nextindex,            transaction,            context          )        );        nextmountindex++;      }      // 更新nextindex      nextindex++;      lastplacednode = reactreconciler.gethostnode(nextchild);    }    // 移除掉不存在的旧子节点,和旧子节点和新子节点不同的旧子节点    for (name in removednodes) {      if (removednodes.hasownproperty(name)) {        updates = enqueue(          updates,          this._unmountchild(prevchildren[name], removednodes[name])        );      }    }  }
4、基于中diff的开发建议基于tree diff:
开发组件时,注意保持dom结构的稳定;即,尽可能少地动态操作dom结构,尤其是移动操作。
当节点数过大或者页面更新次数过多时,页面卡顿的现象会比较明显。
这时可以通过 css 隐藏或显示节点,而不是真的移除或添加 dom 节点。
基于component diff:
注意使用 shouldcomponentupdate() 来减少组件不必要的更新。
对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff的性能消耗。
基于element diff:
对于列表结构,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 react 的渲染性能。
以上就是react中diff算法是什么?diff算法的策略及实现的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product