key的特殊属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes,若是不使用key,Vue会使用一种最大限度减小动态元素而且尽量的尝试修复/再利用相同类型元素的算法。使用key,它会基于key的变化从新排列元素顺序,而且会移除key不存在的元素。有相同父元素的子元素必须有独特的key。重复的key会形成渲染错误。 总之就是一句话,key属性可以提高性能(主要做用于数据更新时)。vue
最多见的用例是结合 v-for:node
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>
复制代码
它也能够用于强制替换元素/组件而不是重复使用它。当你遇到以下场景时它可能会颇有用:git
<transition>
<span :key="text">{{ text }}</span>
</transition>
复制代码
当 text 发生改变时, 会随时被更新,所以会触发过渡。github
使用v-for更新已渲染的元素列表时,默认用就地复用策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,若是修改,则从新渲染这一项,不然复用以前的元素; 咱们在使用的使用常常会使用index(即数组的下标)来做为key,但其实这是不推荐的一种使用方法;算法
举个例子:数组
const list = [
{
id: 1,
name: 'test1',
},
{
id: 2,
name: 'test2',
},
{
id: 3,
name: 'test3',
},
]
复制代码
<div v-for="(item, index) in list" :key="index+'s'" >{{item.name}}</div>
复制代码
上面这种是咱们作项目中经常使用到的一种场景,由于不加key,vue如今直接报错,因此我使用index做为key;下面列举两种常见的数据更新状况bash
1.在最后一条数据后再加一条数据性能
const list = [
{
id: 1,
name: 'test1',
},
{
id: 2,
name: 'test2',
},
{
id: 3,
name: 'test3',
},
{
id: 4,
name: '我是在最后添加的一条数据',
},
]
复制代码
此时前三条数据直接复用以前的,新渲染最后一条数据,此时用index做为key,没有任何问题;ui
2.在中间插入一条数据spa
const list = [
{
id: 1,
name: 'test1',
},
{
id: 4,
name: '我是插队的那条数据',
}
{
id: 2,
name: 'test2',
},
{
id: 3,
name: 'test3',
},
]
复制代码
此时更新渲染数据,经过index定义的key去进行先后数据的对比,发现
以前的数据 以后的数据
key: 0 index: 0 name: test1 key: 0 index: 0 name: test1
key: 1 index: 1 name: test2 key: 1 index: 1 name: 我是插队的那条数据
key: 2 index: 2 name: test3 key: 2 index: 2 name: test2
key: 3 index: 3 name: test3
复制代码
经过上面清晰的对比,发现除了第一个数据能够复用以前的以外,另外三条数据都须要从新渲染;
是否是很惊奇,我明明只是插入了一条数据,怎么三条数据都要从新渲染?而我想要的只是新增的那一条数据新渲染出来就好了
最好的办法是使用数组中不会变化的那一项做为key值,对应到项目中,即每条数据都有一个惟一的id,来标识这条数据的惟一性;使用id做为key值,咱们再来对比一下向中间插入一条数据,此时会怎么去渲染。
以前的数据 以后的数据
key: 1 id: 1 index: 0 name: test1 key: 1 id: 1 index: 0 name: test1
key: 2 id: 2 index: 1 name: test2 key: 4 id: 4 index: 1 name: 我是插队的那条数据
key: 3 id: 3 index: 2 name: test3 key: 2 id: 2 index: 2 name: test2
key: 3 id: 3 index: 3 name: test3
复制代码
如今对比发现只有一条数据变化了,就是id为4的那条数据,所以只要新渲染这一条数据就能够了,其余都是就复用以前的
其实,真正的缘由并非vue怎么怎么,而是由于Virtual DOM 使用Diff算法实现的缘由
下面大体从虚拟DOM的Diff算法实现的角度去解释一下
vue的虚拟DOM的Diff算法大体相同,其核心是基于两个简单的假设:
两个相同的组件产生相似的DOM结构,不一样的组件产生不一样的DOM结构。 同一层级的一组节点,他们能够经过惟一的id进行区分。基于以上这两点假设,使得虚拟DOM的Diff算法的复杂度从O(n^3)降到了O(n)。
例子:
当某一层有不少相同的节点时,也就是列表节点时,Diff算法的更新过程默认状况下也是遵循以上原则。 好比一下这个状况:
咱们但愿能够在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是否是很没有效率?
因此咱们须要使用key来给每一个节点作一个惟一标识,Diff算法就能够正确的识别此节点,找到正确的位置区插入新的节点。
因此一句话,key的做用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue能够区分它们,不然vue只会替换其内部属性而不会触发过渡效果。
参考: