React——虚拟DOM与Diff算法(不用index为key赋值的原因)

React——虚拟DOM与Diff算法(不用index为key赋值的原因)

篇幅较长,但看完定会收获满满~

React的高性能很多来自于它的虚拟DOM,在项目初始化渲染,或处罚setState时,会触发render()函数来进行页面渲染,React并不会一个组件一个组件的向页面中插入DOM元素,而是在内存中创建一个DOM tree,等到所有新节点都放到了内存中的DOM tree中后,React会一次性的将其渲染到页面中,这样就只触发了一次DOM操作;

由于浏览器中DOM操作非常消耗性能,所以减少DOM操作自然也就成了优化性能更好的选择。

而就在rendr函数执行时,React并不会一次将虚拟DOm全部会知道页面中,而是通过比较,看哪些元素变化了,需要绘制,而没有变化的DOM元素就不绘制了,二者比较所使用的算法,就是鼎鼎有名的Diff算法。

Diff算法:
对比两棵DOM树时,首选判断两棵树的根节点,不同类型的根节点元素会有不同的形态;
如果根节点类型(div,table,…)不同,则属性与子元素等其他的就无需比较,直接全部替换就是最优解;
如果根节点类型(div,table,…)相同,则比较两者是否有不同的属性和属性值,只需改变不同的属性值,添加新的属性+属性值,删除减少的属性+属性值,无需更换节点。
在处理完当前节点时,React会继续用同样的方法递归器子节点。

优化Diff算法使用的小技巧:
在子元素末尾列表新增元素 开销较小

举例:

真实DOM:
<ul>
	<li class="li1"></li>
	<li class="li2"></li>
</ul>
虚拟DOM:
<ul>
	<li class="li1"></li>
	<li class="li2"></li>
	<li class="li3"></li>
</ul>

Diff算法计算过程:
真 ----- 虚
ul === ul
li1 === li1
li2 === li2
’ ’ ! == li3

计算过后程序会知道,只需在真实DOM中的<li class=“li2”></li>后面添加一个新元素<li class=“li3”></li>即可。

但如果将<li class=“li3”></li>添加到<li class=“li1”></li>前面呢?

真实DOM:
<ul>
	<li class="li1"></li>
	<li class="li2"></li>
</ul>
虚拟DOM:
<ul>
	<li class="li3"></li>
	<li class="li1"></li>
	<li class="li2"></li>
</ul>

Diff算法计算过程:
真 ----- 虚
ul === ul
li1 !== li3
li2 !== li1
’ ’ ! == li2

计算过后程序会知道,ul下面的所有li均不匹配,所以需要全部替换。

这样就会浪费性能。

但很多时候这种类似的情况我们没办法精确的优化Diff算法。

但这也是可以解决的,就是为子元素添加key属性为其标记唯一性。

比如上面的例子:

真实DOM:
<ul>
	<li class="li1" key="key1"></li>
	<li class="li2" key="key1"></li>
</ul>
虚拟DOM:
<ul>
	<li class="li3" key="key3"></li>
	<li class="li1" key="key1"></li>
	<li class="li2" key="key2"></li>
</ul>

Diff算法认为key值相等的就是相同的元素,所以Diff算法计算过后,React知道只有key="key3"的元素是新元素,key="key1"与key="key2"的两个元素仅仅移动了位置,所以在对应位置插入key="key3"的元素就可以了。

使用key属性的注意事项:

key在当前列表中必须唯一,尽量不要用index与Math.random()为key赋值。个人习惯为每个需要遍历的数据都加上唯一的id值,遍历时用id为key赋值就好了。

如果用index为其赋值,那么虚拟DOM与真实DOM遍历时会分别进行遍历,就会出现两次遍历导致相同元素会有不同key值的情况,此时Diff算法就会由于二者key值不同而认为它们是不同的:

真实DOM:
<ul>
	<li class="li1" key="0"></li>
	<li class="li2" key="1"></li>
	<li class="li3" key="2"></li>
	<li class="li4" key="3"></li>
</ul>
虚拟DOM(删除了<li class="li2" key="2"></li>):
<ul>
	<li class="li1" key="0"></li>
	<li class="li3" key="1"></li>
	<li class="li4" key="2"></li>
</ul>

Diff算法会由于key值的缘故认为
<li class=“li3” key=“2”></li> <li class=“li4” key=“3”></li>

<li class=“li3” key=“1”></li> <li class=“li4” key=“2”></li>
是不同的,要全部替换,但实际上其实相同的。

而Math.random()会为真实DOM与虚拟DOM的所有元素均添加不同的key,这意义并不大,或者说,只是让react不报错了而已;
而且虽然Math.random()生成的数字范围极大,但也有很小的概率生成相同的数字,这就很有可能使Diff算法误认为两个不同的元素是相同的,一个严谨的项目不可以出现这种可以避免的漏洞!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue和React都是流行的前端框架,它们在实现上有很多相似之处,但也有很多不同之处。其中,最明显的区别就是在处理视图更新时采用的不同的数据绑定方式和不同的虚拟DOM实现。 Diff算法: Vue采用的是双向绑定机制,即当数据发生变化时,Vue会自动更新视图,这是通过Object.defineProperty()实现的。Vue的更新机制是基于依赖追踪的。当数据发生变化时,Vue会重新渲染视图,并使用Diff算法来计算哪些元素需要更新,Diff算法的核心是比较新旧虚拟DOM树的差异。 React采用的是单向数据流机制,即当数据发生变化时,React不会直接更新视图。相反,React会创建一个新的虚拟DOM树,然后使用Diff算法比较新旧虚拟DOM树的差异,并且只更新发生变化的节点。React中的Diff算法被称为Reconciliation算法,它是基于两个假设:1.相同类型的组件会生成相似的树形结构;2.不同类型的组件会生成不同的树形结构。 虚拟DOM: Vue的虚拟DOM是轻量级的,只包含必要的信息,比如标签名、属性和子节点等。Vue中的虚拟DOM是通过render()函数生成的,它是一个JavaScript对象。当数据发生变化时,Vue会重新生成一个新的虚拟DOM树,并使用Diff算法比较新旧虚拟DOM树的差异。Vue中的虚拟DOM只关注视图和数据之间的关系,而不关注其他方面,比如事件处理等。 React虚拟DOM也是一个JavaScript对象,但它比Vue的虚拟DOM更加完整和复杂。React虚拟DOM包含完整的组件层级信息、组件状态和事件处理等。当数据发生变化时,React会重新生成一个新的虚拟DOM树,并使用Diff算法比较新旧虚拟DOM树的差异。与Vue不同,React虚拟DOM不仅关注视图和数据之间的关系,还关注其他方面,比如事件处理等。 总的来说,Vue和ReactDiff算法虚拟DOM的实现上有所不同。Vue采用的是双向绑定机制,它的虚拟DOM只关注视图和数据之间的关系;React采用的是单向数据流机制,它的虚拟DOM包含完整的组件层级信息、组件状态和事件处理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值