具体可见: https://juejin.im/post/6844904133430870024
在react中,如果要渲染一个列表,我们会用map()函数将数组循环处理然后渲染到DOM,在处理循环时,“key” 是一个你需要包含的特殊字符串属性。
很多时候,我们会使用当前列表的索引为key,但这样做真的好吗?
要讨论这个问题,我们要从react的原理说起,我们react的执行步骤一般是:用state和jsx模板生成虚拟DOM,然后用虚拟DOM生成真实的 DOM,当我们state发生变化时,render函数执行,生成新的 虚拟DOM,然后比较新旧虚拟DOM的区别,找到区别,然后直接操作DOM,改变有区别的内容,这样比传统的操作DOM,极大的提升了性能。
再说虚拟DOM,虚拟DOM其实就是一个JS对象(['div',{class:'app'},'item']),虚拟DOM核心之一是diff算法,diff算法的核心之一是同层对比,如图:
如果在第一层div时就有出现区别,那么对比结束,直接更新真实DOM中对应的当前节点,以此类推。。。
再说说key,假设我们在state中有一个列表[a,b,c],在遍历渲染时用索引作为key,那么就是这样:
a 0
b 1
c 2
如果我们执行一个操作,点击删除数组中的a, 我们的列表就是[b,c],在遍历渲染时仍然会用索引作为key,结果如下:
b 0
c 1
如图 ,我们如果不用索引为key , 程序能快速的对比出差异,反之也能对出差异,但是必须对比整个虚拟DOM,
这样,程序仍然能正常执行,只不过大大消耗了新旧虚拟DOM的对比的性能,并可能导致组件状态问题。
再举一个vue的例子:为什么不能用index作为key?
举个栗子:
<template> <div v-for="(item, index) in list" :key="index" >{{item.name}}</div> </template>
const list = [ { id: 1, name: "Person1" }, { id: 2, name: "Person2" }, { id: 3, name: "Person3" }, { id:4, name:"Person4" } ];
此时,删除 “Person4” 是正常的,但是如果我删除 “Person2” 就会出现问题。
删除前
删除后
这个时候,除了 Person1 之外,剩下的 Person3、Person4,因为被发现与相应 key
的绑定关系有变化,所以被重新渲染,这会影响性能。
如果此时 list
的 item
是 select 的选项,其中 Person3 是选中的,这个时候 Person2 被删除了,用 index 作为 key 就会变成是 Person4 选中的了,这就产生了bug。
如果使用唯一id作为key,删除 Person2 后,剩下的元素因为与 key
的关系没有发生变化,都不会被重新渲染,从而达到提升性能的目的。此时,list
的 item
作为 select 的选项,也不会出现上面所描述的bug。