Vue中的列表渲染:
Vue中通过 v-for 对数组、对象、字符串等进行遍历并生成列表项 li。
<li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
其中persons为Vue中data的数据,p为形参,表示遍历时每一个元素。每遍历一次,就生成一个列表项 li。而li的内容就可以通过插值语法添加p中的属性,类似for in循环。
此处的 :key 即 bind:key 给每个列表项 li 绑定了key属性,并赋予它p的唯一标记属性id,在react和Vue3中,只要通过遍历生成了多个同样结构的元素,就必须给每个结构添加唯一标识,即这里的key。
在 v-for 进行遍历时其实可以接收到两个参数:
<li v-for="(p,index) in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
第二个参数为index索引值,即当前遍历元素在原数组(对象、字符串等)中的索引值是多少。这里就很容易会想到将 index 赋予 key 当作当前元素的唯一标识符,但这样操作很可能会出现问题。
用index直接作为元素的 key 值会出现的问题:
若此时有这么一个场景:在每个 li 中含有一个输入框(小demo忽略样式难看谢谢)
若是此时往第一个元素插入一个新数据,就会出现以下状况:
Key的工作原理:
key主要用于虚拟DOM对比算法,即diff算法:
当数据更新时,Vue会根据新数据生成新的虚拟DOM,并会以key为标志对比新生成的虚拟DOM和旧虚拟DOM,对比对象为新旧虚拟DOM中key值相同的两个元素。
若每个元素上有多个节点,如文本节点、<span>、<input>等,则会逐一比较,若比较结果为不相同,则会通过虚拟DOM将该节点转化为真实DOM;若比较结果为相同,则会直接复用原来的真实DOM(不需要重新生成)。
值得注意的是,这里的比较是针对虚拟DOM的比较而不是真实DOM,所以这里即使文本框是有内容的,虚拟DOM是检查不出来的,虚拟DOM只会比较节点本身的属性。
若以index作为key值,则在添加数据时,若数据被添加在原数据的前面,则会导致index发生改变,而原虚拟DOM的key值是不会改变的,这样就会导致原来本身已加载的节点无法复用,需要重新通过虚拟DOM生成。更重要的是,若在每个li中含有这样的 input 节点,由于虚拟DOM无法比较其中的内容,则会直接复用过来,就会出现上图的问题。
而如果使用自定的唯一属性作为元素的 key 值,数据的增减不会改变其唯一属性,则不会出现上述问题。