6.3 可以对数组数据进行唯一标识的作为key
6.3.1 示例
在提供的数据数组中,每个对象的 id 可以对该对象进行唯一标识,所以可以使用 id 作为key。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="root"> <button @click="addPerson">添加</button> <ul> <li v-for="(person, index) in persons" :key="person.id"> {{index}}: {{person.name}} -- {{person.age}} <input type="text"> </li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <script> const vm = new Vue({ el: '#root', data: { persons: [ {id: 101, name: 'ZS', age: 18}, {id: 102, name: 'LS', age: 19}, {id: 103, name: 'WW', age: 20} ] }, methods: { addPerson() { this.persons.unshift({id: 104, name: 'TOM', age: 21}) } }, }) </script> </html>
提供观察结果发现,新的人员信息和新的输入框在加入页面显示后,没有出现不匹配的问题。
6.3.2 解释
6.4 key的作用
- 虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
- (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
- ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面。
6.5 用index作为key可能会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
- 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:
- 会产生错误DOM更新 ==> 界面有问题。
如果不对数据进行破坏顺序操作,则使用index作为key不会引发问题。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="root"> <button @click="addPerson">添加</button> <ul> <li v-for="(person, index) in persons" :key="idnex"> {{index}}: {{person.name}} -- {{person.age}} <input type="text"> </li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <script> const vm = new Vue({ el: '#root', data: { persons: [ {id: 101, name: 'ZS', age: 18}, {id: 102, name: 'LS', age: 19}, {id: 103, name: 'WW', age: 20} ] }, methods: { addPerson() { this.persons.push({id: 104, name: 'TOM', age: 21}) } }, }) </script> </html>
6.6 开发中如何选择key
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
7. 列表过滤
7.1 监视属性实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"> <!-- 使用 v-model 进行双向绑定 获取用户的输入 --> <input type="text" placeholder="请输入姓名..." v-model="keyWord" /> <ul> <li v-for="(person, index) in filterPerson" :key="person.id">{{index}}: {{person.name}} -- {{person.age}} -- {{person.sex}}</li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <script> const vm = new Vue({ el: '#root', data: { // 保存用户的输入 keyWord: '', persons: [ { id: '001', name: '马冬梅', age: 19, sex: '女' }, { id: '002', name: '周冬雨', age: 20, sex: '女' }, { id: '003', name: '周杰伦', age: 21, sex: '男' }, { id: '004', name: '温兆伦', age: 22, sex: '男' }, ], // 用于保存过滤后的数组 filterPerson: [], }, watch: { // 监视用户的输入 keyWord: { // 初始化时立即执行一次, // 初始用户没有输入,使用空字符串进行过滤,所有的数组元素都保留 // 显示数组中的全部元素 immediate: true, handler(newVal) { // 对数组进行过滤,并将过滤后的结果保存到filterPerson中 this.filterPerson = this.persons.filter((person) => { // 查询姓名中是否包含用户输入的字符串 // 空字符串会返回0 // 包含输入的字符串则返回对应的索引 // 不包含返回-1 return person.name.indexOf(newVal) !== -1 }) }, }, }, }) </script> </html>
7.2 计算属性实现
计算属性与监视属性都能实现的功能,优先使用计算属性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"> <!-- 使用 v-model 进行双向绑定 获取用户的输入 --> <input type="text" placeholder="请输入姓名..." v-model="keyWord" /> <ul> <li v-for="(person, index) in filterPerson" :key="person.id">{{index}}: {{person.name}} -- {{person.age}} -- {{person.sex}}</li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <script> const vm = new Vue({ el: '#root', data: { // 保存用户的输入 keyWord: '', persons: [ { id: '001', name: '马冬梅', age: 19, sex: '女' }, { id: '002', name: '周冬雨', age: 20, sex: '女' }, { id: '003', name: '周杰伦', age: 21, sex: '男' }, { id: '004', name: '温兆伦', age: 22, sex: '男' }, ] }, computed: { // 由于页面中使用到了该计算属性,所以页面初始化时会调用一次 // 由于该计算属性使用了keyWord,所以当keyWord变化时,也会自动调用该计算属性 filterPerson() { // 对数组进行过滤 return this.persons.filter((person)=>{ // 判断姓名是否包含用户的输入 return person.name.indexOf(this.keyWord) !== -1 }) } } }) </script> </html>
8. 列表排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"> <!-- 使用 v-model 进行双向绑定 获取用户的输入 --> <input type="text" placeholder="请输入姓名..." v-model="keyWord" /> <button @click="sortType = 2">年龄升序</button> <button @click="sortType = 1">年龄降序</button> <button @click="sortType = 0">原顺序</button> <ul> <li v-for="(person, index) in filterPerson" :key="person.id">{{index}}: {{person.name}} -- {{person.age}} -- {{person.sex}}</li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <script> const vm = new Vue({ el: '#root', data: { // 保存用户的输入 keyWord: '', // 数据显示的顺序 // 默认为0(原顺序) 1降序 2升序 sortType: 0, persons: [ { id: '001', name: '马冬梅', age: 19, sex: '女' }, { id: '002', name: '周冬雨', age: 20, sex: '女' }, { id: '003', name: '周杰伦', age: 21, sex: '男' }, { id: '004', name: '温兆伦', age: 22, sex: '男' }, ], }, computed: { filterPerson() { // 保存过滤后的数据 const personArr = this.persons.filter((person) => { return person.name.indexOf(this.keyWord) !== -1 }) // 对过滤后的数据进行排序 // 如果需要的数组顺序不为原顺序,则进行排序 if (this.sortType) { // 排序 personArr.sort((p1, p2) => { // 判断是要降序还是升序 return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age }) } // 然后数组 return personArr }, }, }) </script> </html>