更新记录
- 2023-09-11:对于“如何选择key”作了补充。
列表渲染
React
,Vue
中的key
有什么作用?
-
虚拟DOM
中key
的作用:key
是虚拟DOM
中对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM
,随后Vue进行新虚拟DOM
与旧虚拟DOM
的差异比较,比较规则如下: -
比较规则:
- 旧
虚拟DOM
中找到了与新虚拟DOM
相同的key
- 若
虚拟DOM
中内容没变,直接使用之前的真实DOM
。 - 若
虚拟DOM
中内容改变,则生成新的真实DOM
,随后替换掉页面中之前的真实DOM
。
- 若
- 旧
虚拟DOM
中未找到与新虚拟DOM
相同的key
- 创建新的
真实DOM
,随后渲染到页面。
- 创建新的
图解:
- 旧
-
使用index作为
key
可能会引发的问题- 若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的
真实DOM
更新——页面效果正常,但浪费资源且效率低。 - 若结构中还包含输入类的DOM:会产生错误DOM更新——界面出问题。
- 若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的
-
开发中如何选择
key
-
最好使用每条数据的唯一标识作为
key
,比如id、手机号、身份证号、学号等唯一值。 -
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用
index
作为key
是没有问题的。如果列表中进行数据更新、排序或过滤等操作,使用索引作为
key
,可能会导致列表项的重新排序或过滤时出现问题,因为索引是根据项的位置而不是唯一标识来分配的。代码示例:
<template> <div> <ul> <li v-for="item in items" :key="item.id"> {{ item.name }} </li> </ul> </div> </template> <script> export default { data() { return { items: [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' } ] }; } }; </script>
通过交换数组中最后一项和第一项的位置来改变列表的顺序:
this.items.unshift(this.items.pop());
然而,由于我们在
v-for
循环中使用索引作为key
,当我们执行上述代码时,Vue会将最后一项的<li>
元素与第一项的<li>
元素进行交换,但是它们的key
值并没有改变。这样就会导致Vue错误地认为这两个元素是相同的,从而无法正确地更新DOM,结果可能是两个元素的内容混乱或丢失。 -
即使没有写
:key="index"
,Vue 也会帮我们把index
作为key
-
如果使用索引作为
key
,当列表项的顺序发生变化时,Vue会重新创建和销毁组件实例。这可能会导致组件的状态丢失,例如输入框中的内容或选中的状态。使用唯一标识作为key
可以避免这个问题,因为Vue会根据key
来复用和保持组件实例的状态。
-
图解:
数据监视
-
Vue会监视
data
中所有层次的数据。 -
如何监测对象中的数据?
通过
setter
实现监视,且要在new Vue()
时就传入要监测的数据-
对象创建后追加的属性,Vue默认不做响应式处理。直接在
vm._data.student
或者vm
中添加该属性,是做不到响应式的,没有getter
和setter
方法。 -
如需给后添加的属性做响应式,请使用如下API
Vue.set(target, propertyName/index, value)
vm.$set(target, propertyName/index, value)
-
-
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue修改数组中的某个元素一定要用如下方法
push()
pop()
unshift()
shift()
splice()
sort()
reverse()
这几个方法被 Vue 重写了Vue.set()
或vm.$set()
。push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
pop() 方法用于删除并返回数组的最后一个元素。
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
sort() 方法用于对数组的元素进行排序。
reverse() 方法用于颠倒数组中元素的顺序。
替换数组
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
concat() 方法用于连接两个或多个数组。
slice() 方法可从已有的数组中返回选定的元素。
其他
split() 方法用于把一个字符串分割成字符串数组。
注意:
Vue.set()
和vm.$set()
不能给vm
或vm
的根数据对象(data
等)添加属性。通过数组索引直接赋值去修改元素,页面并不会更新,因为Vue并没有像给对象一样给数组中的每个元素提供
getter
和setter
函数,Vue无法监测到数据的改变。但是若通过直接赋值修改数组中某个元素的属性时,页面会更新,因为Vue对数组中每个元素的属性都有其
setter
和getter
方法。