• home > webfront > ECMAS > vue >

    vue、react中key有何区别?循环渲染优化与Vue.delete删除

    Author:zhoulujun Date:

    key的作用主要是为了高效的更新虚拟DOM。无论是vue还是react,如果数据源不满足key值唯一这样的需求,我们可以在渲染之前为数据源手动添加唯一id,但不能在渲染时添加。删除数组用delete和Vue delete有什么区别?

    vue和react都是在操作虚拟dom,并且根据key进行新旧dom对比,从而更新dom。vue、react中循环遍历为什么会有key,key在其中的作用是什么?key的作用主要是为了高效的更新虚拟DOM。

    为了虚拟dom能够快速找准对应的节点,进行对比,极大的提升虚拟dom对比速度,也减少了不必要的遍历。

    当然,vue与react在具体细节上,还是有区别的。

    Vue中key属性的作用

    vue是通过比对组件自身新旧vdom进行更新的。key的作用是辅助判断新旧vdom节点在逻辑上是不是同一个对象。key值的存在保证了唯一性,可以用于dom的重新渲染或是就地复用

    • 在列表渲染(v-for)时使用key属性

    • 使用key属性强制替换元素

    官方文档中说:

    key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

    当 Vue.js 用v-for正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

    在列表渲染时使用key属性

    例如 一个数组data=[1,2,3],删除中间一个元素,data.splice(1,1),但Vue会认为你做了两件事:

    1. 把2变成了3

    2. 然后把3删除了

    看看这两个数组:[1,2,3] 和 [1,3]

    人类会说,这不就是少了个 2 吗?

    但是计算机会怎么对比数组?遍历!

    首先对比 1 和 1,发现「1 没变」;然后对比 2 和 3,发现「2 变成了 3」;最后对比 undefined 和 3,发现「3 被删除了」。

    所以计算机的结论是:「2 变成了 3」以及「3 被删除了」,有毛病吗?没毛病。既然「1 没变」,那么就地复用之前的 1 和三角形就好了。

    • 既然「2 变成了 3」,那么正方形左边的 2,当然要改成 3。里面的正方形就地复用(正方形没有被删除)。因为正方形三个字是孙子元素的 data,不受「2 变成 3」的影响,所以可以就地复用。

    • 既然「3 被删除了」,之前的「圆形」当然应该被删掉,里面的子元素也要删除。

    vue v-for key的作用v2-6f6d53572bf07e71b6ac83fd8049175a_hd.jpg

    为什么不能用 index 作为 key

    如果你用 index 作为 key,那么在删除第二项的时候,index 就会从 1 2 3 变成 1 2(而不是 1 3),那么 Vue 依然会认为你删除的是第三项。也就是会遇到上面一样的 bug。

    永远不要!除非你是大神。能清楚地知道如何解决 index 做 key 带来的 bug。

    使用key属性强制替换元素

    key属性还有另外一种使用方法,即强制替换元素,从而可以触发组件的生命周期钩子或者触发过渡。因为当key改变时,Vue认为一个新的元素产生了,从而会新插入一个元素来替换掉原有的元素

    <transition>
        <span :key="text">{{text}}</span>
    </transition>

    这里如果text发生改变,整个<span>元素会发生更新,因为当text改变时,这个元素的key属性就发生了改变,在渲染更新时,Vue会认为这里新产生了一个元素,而老的元素由于key不存在了,所以会被删除,从而触发了过渡。

    假如没有key属性:那么当text改变时,Vue会复用元素,只改变<span>元素的内容,而不会有新的元素被添加进来,也不会有旧的元素被删除。同理,key属性被用在组件上时,当key改变时会引起新组件的创建和原有组件的删除,此时组件的生命周期钩子就会被触发。

    删除数组用delete和Vue.delete有什么区别?

    delete:只是被删除数组成员变为 empty / undefined,其他元素键值不变,不能触发vue的响应式更新

    Vue.delete:直接删了数组成员,并改变了数组的键值(对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制)

    React 为什么要使用key

    React 中的 element diff 算法

    当在数组或者迭代器中循环渲染元素的时候,其实是用到了 React 的 element diff 算法,,当节点处于同一层级时,React diff 提供了三种节点操作,分别为:INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和 REMOVE_NODE(删除)。

    假如props.dataLists = ['a', 'b', 'c', 'd'],当现在由外部传入的dataLists修改为 ['b','a','d','c'] 的时候,此时,如果按照原始的diff算法,对比旧的props.dataLists = ['a', 'b', 'c', 'd'];,发现 第一个位 b != a,则创建并且插入 b 到新的集合里面,删除老得a(这里我们假设 abcd 也代表一个element)如此类推,创建插入了 a,d,c删除了b,c,d;

    • old:  a, b, c, d

    • new: b, a, d, c

    假设这里有10000个 elements, 这里的开销大到不能想象,而且仔细的你可能已经发现了,其实上面的 element并没有发生变化,他们仅仅是发生了位置的变化,但是却产生了非常大开销的删除、创建和删除操作,说白了,其实我们只要交换以下几个 element 的位置就好了。

    所以,针对这样一个优化,React 提出了这样的优化策略。

            允许开发者对同一层级的同组子节点,添加唯一 key 进行区分

    新老集合所包含的节点,老集合进行 diff 差异化对比,通过 key 发现新老集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将老集合中节点的位置进行移动,更新为新集合中节点的位置,此时 React 给出的 diff 结果为:b、d 不做任何操作,a、c进行移动操作,即可。

    react官方文档是这样描述key的:

    Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

    react的diff算法是把key当成唯一id然后比对组件的value来确定是否需要更新的,所以如果没有key,react将不会知道该如何更新组件。

    你不传key也能用是因为react检测到子组件没有key后,会默认将数组的索引作为key。

    react根据key来决定是销毁重新创建组件还是更新组件,原则是:

    • key相同,组件有所变化,react会只更新组件对应变化的属性。

    • key不同,组件会销毁之前的组件,将整个组件重新渲染。

    使用index做key存在的问题

    受控组件

    使用index作为key,可能表面上不会有什么问题,实际上性能会受很大的影响。一个数组data=[1,2,3],改变数组顺序,三个子组件都会被重新渲染。(这里的重新渲染不是销毁,因为key还在)相反,

    使用唯一id作为key:子组件的值和key均未发生变化,只是顺序发生改变,因此react只是将他们做了移动,并未重新渲染。

    非受控组件

    像input这样可以由用户任意改变值,不受我们控制的组件,在使用了index作为key时可能会发生问题,属性不会跟着修改。


    使用index的情况

    如果组件单纯的用于展示,不会发生其他变更,那么使用index或者其他任何不相同的值作为key是没有任何问题的,因为不会发生diff,就不会用到key。


    无论是vue还是react,如果数据源不满足key值唯一这样的需求,我们可以在渲染之前为数据源手动添加唯一id,但不能在渲染时添加。

    参考文章:

    React 源码剖析系列 - 不可思议的 react diff https://segmentfault.com/a/1190000017152570

    Vue中key属性的作用 https://blog.csdn.net/zyj362633491/article/details/86654014

    关于Vue v-for中的:key作用 https://blog.csdn.net/shicl/article/details/81392385

    react中key的正确使用方式 https://segmentfault.com/a/1190000017152570

    diff算法1
    diff算法2


    转载本站文章《vue、react中key有何区别?循环渲染优化与Vue.delete删除》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue/8295.html