v-for 指令
用于展示列表数据
语法:<li v-for=(item, index) in xxx :key="yyy">
,这里 key 可以是index,更好的是遍历对象的唯一标识
如果 key 写的是 index,前面可以不写 index 也能用
可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <title > 基本列表</title > <script type ="text/javascript" src ="../js/vue.js" > </script > <div id ="root" > <h3 > 人员列表(遍历数组)</h3 > <ul > <li v-for ="(p,index) of persons" :key ="index" > {{ p.name }}-{{ p.age }}</li > </ul > <h3 > 汽车信息(遍历对象)</h3 > <ul > <li v-for ="(value,k) of car" :key ="k" > {{ value }}--{{ k }}</li > </ul > <h3 > 测试遍历字符串(用得少)</h3 > <ul > <li v-for ="(char,index) of str" :key ="index" > {{ char }}-{{ index }}</li > </ul > <h3 > 测试遍历指定次数(用得少)</h3 > <ul > <li v-for ="(number,index) of 5" :key ="index" > {{ number }}--{{ index }}</li > </ul > </div > <script type ="text/javascript" > Vue.config.productionTip = false ; new Vue({ el: '#root' , data: { persons: [ { id: '001' , name: '张三' , age: 18 }, { id: '002' , name: '李四' , age: 19 }, { id: '003' , name: '王五' , age: 20 } ], car: { name: '奥迪A8' , price: '70万' , color: '黑色' }, str: 'hello' } })</script >
2 key 的作用与原理
vue 中的 key 有什么作用?(key的内部原理) 就是 vue 的虚拟 dom,vue 会根据 data 中的数据生成虚拟 dom,如果是第一次生成页面,就将虚拟 dom 转成真实dom,在页面展示出来。
虚拟dom有啥用? 每次 vm._data
中的数据更改,都会触发生成新的虚拟 dom,新的虚拟 dom 会跟旧的虚拟 dom 进行比较,如果有相同的,在生成真实 dom 时,这部分相同的就不需要重新生成,只需要将两者之间不同的 dom 转换成真实 dom,再与原来的真实 dom 进行拼接。 我的理解是虚拟 dom 就是起到了一个 dom 复用的作用,还有避免重复多余的操作,下文有详细解释。
而 key 有啥用? key 是虚拟 dom 的标识。
虚拟 DOM 中 key 的作用 key 是虚拟 DOM 对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟DOM】, 随后 Vue 进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM!
若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM。
旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key
用 index 作为 key 可能会引发的问题 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低。
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <div id ="root" > <h2 > 人员列表(遍历数组)</h2 > <button @click.once ="add" > 添加一个老刘</button > <ul > <li v-for ="(p,index) of persons" :key ="index" > {{p.name}}-{{p.age}} <input type ="text" > </li > </ul > </div > <script type ="text/javascript" > Vue.config.productionTip = false ; new Vue({ el: '#root' , data: { persons: [ { id: '001' , name: '张三' , age: 18 }, { id: '002' , name: '李四' , age: 19 }, { id: '003' , name: '王五' , age: 20 } ] }, methods: { add() { const p = { id: '004' , name: '老刘' , age: 40 }; this .persons.unshift(p) } }, });</script >
初始数据
1 2 3 4 5 persons: [ { id: '001', name: '张三', age: 18 }, { id: '002', name: '李四', age: 19 }, { id: '003', name: '王五', age: 20 } ]
vue根据数据生成虚拟 DOM
初始虚拟 DOM
1 2 3 <li key ='0' > 张三-18<input type ="text" > </li > <li key ='1' > 李四-19<input type ="text" > </li > <li key ='2' > 王五-20<input type ="text" > </li >
将虚拟 DOM 转为 真实 DOM
在 persons 数组最前面添加上 this.persons.unshift({ id: '004', name: '老刘', age: 40 })
新数据:
1 2 3 4 5 6 persons: [ { id: '004', name: '老刘', age: 40 }, { id: '001', name: '张三', age: 18 }, { id: '002', name: '李四', age: 19 }, { id: '003', name: '王五', age: 20 } ]
vue根据数据生成虚拟 DOM
1 2 3 4 <li key ='0' > 老刘-30<input type ="text" > </li > <li key ='1' > 张三-18<input type ="text" > </li > <li key ='3' > 李四-19<input type ="text" > </li > <li key ='4' > 王五-20<input type ="text" > </li >
将虚拟 DOM 转为 真实 DOM
因为老刘被插到第一个,重刷了 key 的值,vue Diff 算法根据 key 的值 判断 虚拟DOM 全部发生了改变,然后全部重新生成新的 真实 DOM。实际上,张三,李四,王五并没有发生更改,是可以直接复用之前的真实 DOM,而因为 key 的错乱,导致要全部重新生成,造成了性能的浪费。
如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
这回造成的就不是性能浪费了,会直接导致页面的错误
总结
最好使用每条数据的唯一标识作为 key, 比如id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
面试题 面试题:reactvue中的key有什么作用?(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是没有问题的
3 列表过滤 watch 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <div id ="root" > <h2 > 人员列表</h2 > <input type ="text" placeholder ="请输入名字" v-model ="keyWord" > <ul > <li v-for ="(p,index) of filPersons" :key ="p.id" > {{ p.name }}-{{ p.age }}-{{ p.sex }} </li > </ul > </div > <script type ="text/javascript" > Vue.config.productionTip = false ; 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: '男' } ], filPersons: [] }, watch: { keyWord: { immediate: true , handler(val) { this .filPersons = this .persons.filter((p ) => { return p.name.indexOf(val) !== -1 }) } } } }) </script >
注意:indexOf
对于空串的结果是0,这样上面代码初始过滤就是全部数据
computed 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <div id ="root" > <h2 > 人员列表</h2 > <input type ="text" placeholder ="请输入名字" v-model ="keyWord" > <ul > <li v-for ="(p,index) of filPersons" :key ="p.id" > {{ p.name }}-{{ p.age }}-{{ p.sex }} </li > </ul > </div > <script type ="text/javascript" > Vue.config.productionTip = false ; 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: { filPersons() { return this .persons.filter((p ) => { return p.name.indexOf(this .keyWord) !== -1 }) } } }) </script >
4 列表排序 有时为了减少服务器压力,能在前端做的尽量在前端做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <div id ="root" > <h2 > 人员列表</h2 > <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 ="(p,index) of filPersons" :key ="p.id" > {{p.name}}-{{p.age}}-{{p.sex}} <input type ="text" > </li > </ul > </div > <script type ="text/javascript" > Vue.config.productionTip = false ; new Vue({ el: '#root' , data: { keyWord: '' , sortType: 0 , persons: [ { id: '001' , name: '马冬梅' , age: 30 , sex: '女' }, { id: '002' , name: '周冬雨' , age: 31 , sex: '女' }, { id: '003' , name: '周杰伦' , age: 18 , sex: '男' }, { id: '004' , name: '温兆伦' , age: 19 , sex: '男' } ] }, computed: { filPersons() { const arr = this .persons.filter((p ) => { return p.name.indexOf(this .keyWord) !== -1 }); if (this .sortType) { arr.sort((p1, p2 ) => { return this .sortType === 1 ? p2.age - p1.age : p1.age - p2.age }) } return arr } } })</script >