1.13.列表渲染
1.13.1.基本列表
v-for指令
- 用于展示列表数据
- 语法:
- ,这里key可以是index,更好的是遍历对象的唯一标识
- 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>基本列表</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!--v-for指令:1.用于展示列表数据2.语法: v-for="(item, index) in xxx" :key="yyy"3.可遍历: 数组、对象、字符串 (用的很少) 、指定次数(用的很少)--><!-- 准备好一个容器 --><div id="root"><!-- 遍历数组 --><h3>人员列表(遍历数组)</h3><ul> <li v-for="(p, index) of personList" :key="index">{{ p.name }}-{{ p.age }}</li> </ul><!-- 遍历对象 --> <h3>汽车信息(遍历对象)</h3> <ul> <li v-for="(value, k) of car" :key="k">{{ k }}-{{ value }}</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">{{ index }}-{{ number }}</li></ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = falsenew Vue({ el: '#root', data:{personList: [ { id: '001', name: '张三', age: 18 }, { id: '002', name: '李四', age: 19 }, { id: '003', name: '王五', age: 20 } ],car: { name: '奥迪A8', price: '70万', color: '黑色' }, str: 'hello'},});</script>
</body>
</html>
1.13.2.key 的作用与原理
面试题:react,vue中的key有什么作用?(key的内部原理)
- 虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下
- 对比规则
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- 用index作为key可能会引发的问题
- 若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低
- 若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
- 开发中如何选择key?
- 最好使用每条数据的唯一标识作为key,比如 id、手机号、身份证号、学号等唯一值
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>key的原理</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!--面试题: vue中的key有什么作用? (key的内部原理)1.虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】随后vue进行【新虚拟DOM】 与【旧虚拟DOM】 的差异比较,比较规则如下:2.对比规则:(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:i.若虚拟DOM中内容没变,直接使用之前的真实DOM!ii.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。3.用index作为key可能会引发的问题:1.若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。2.如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。4.开发中如何选择key?:1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示.使用index作为key是没有问题的。--> <!-- 准备好一个容器 --><div id="root"><!-- 遍历数组 --><h3>人员列表(遍历数组)</h3><button @click.once="add">添加一个老刘</button><ul> <li v-for="(p, index) of personList" :key="index">{{ p.name }}-{{ p.age }}</li> </ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = falsenew Vue({ el: '#root', data:{personList: [ { 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>
</body>
</html>
1.13.3.列表过滤
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>列表过滤</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><!-- 遍历数组 --><h3>人员列表</h3><input type="text" placeholder="请输入名字" v-model="keyWord"><ul> <li v-for="(p,index) of filPersonList" :key="p.id"> {{ p.name }}-{{ p.age }}-{{ p.sex }} </li> </ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = false// 用 watch 实现// new Vue({ // el: '#root', // data:{// keyWord: '',// personList: [// { id: '001', name: '马冬梅', age: 19, sex: '女' },// { id: '002', name: '周冬雨', age: 20, sex: '女' }, // { id: '003', name: '周杰伦', age: 21, sex: '男' },// { id: '004', name: '温兆伦', age: 22, sex: '男' }// ],// filPersonList: []// },// watch: { // keyWord: { // immediate: true, // handler(val) {// this.filPersonList = this.personList.filter((p) => { // return p.name.indexOf(val) !== -1;// }// )} // } // }// });// 用 computed 实现new Vue({ el: '#root', data:{keyWord: '',personList: [{ 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: { filPersonList() { return this.personList.filter((p) => { return p.name.indexOf(this.keyWord) !== -1;});} }});</script>
</body>
</html>
1.13.4. 列表排序
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>列表排序</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><!-- 遍历数组 --><h3>人员列表</h3><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 filPersonList" :key="p.id"> {{ p.name }}-{{ p.age }}-{{ p.sex }}<input type="text"></li> </ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = falsenew Vue({ el: '#root', data:{keyWord: '',sortType: 0, // 0原顺序 1降序 2升序personList: [{ id: '001', name: '马冬梅', age: 19, sex: '女' },{ id: '002', name: '周冬雨', age: 31, sex: '女' }, { id: '003', name: '周杰伦', age: 18, sex: '男' },{ id: '004', name: '温兆伦', age: 22, sex: '男' }]},computed: { filPersonList() {const arr = this.personList.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>
</body>
</html>
1.13.5. Vue 数据监视
更新时的一个问题
this.persons[0] = {id:‘001’,name:‘马老师’,age:50,sex:‘男’} 更改data数据,Vue不监听,模板不改变
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>更新时的一个问题</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><!-- 遍历数组 --><h3>人员列表</h3><button @click="updateMei">更新马冬梅的信息</button><ul> <li v-for="(p,index) of filPersonList" :key="p.id"> {{ p.name }}-{{ p.age }}-{{ p.sex }}<input type="text"></li> </ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = falsenew Vue({ el: '#root', data:{keyWord: '',sortType: 0, // 0原顺序 1降序 2升序personList: [{ id: '001', name: '马冬梅', age: 19, sex: '女' },{ id: '002', name: '周冬雨', age: 31, sex: '女' }, { id: '003', name: '周杰伦', age: 18, sex: '男' },{ id: '004', name: '温兆伦', age: 22, sex: '男' }]},methods: {updateMei() {// this.persons[0].name = '马老师' // 奏效 // this.persons[0].age = 50 // 奏效 // this.persons[0].sex = '男' // 奏效 // this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} // 不奏效this.persons.splice(0, 1, { id: '001', name: '马老师', age: 50, sex: '男' })}}});</script>
</body>
</html>
模拟一个数据监测
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>模拟一个数据检测</title>
</head>
<body><script type="text/javascript">let data = { name: '尚硅谷', address: '北京'}// 创建一个监视的实例对象function Observer(obj) {// 汇总对象中所有的属性形成一个数组const keys = Object.keys(obj)// 遍历keys.forEach((k) => {Object.defineProperty(this, k, {get() {return obj[k]},set(val) {console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`);obj[k] = val;}})})}// 创建一个监视的实例对象,用于监视data中属性的变化 const obs = new Observer(data);console.log(obs);// 准备一个vm实例对象 let vm = {};vm._data = data = obs;</script>
</body>
</html>
原理
- vue会监视data中所有层次的数据
- 如何监测对象中的数据?
- 通过setter实现监视,且要在**new Vue()**时就传入要监测的数据
- 对象创建后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API
- Vue.set(target, propertyName/index, value)
- vm.$set(target, propertyName/index, value)
- 通过setter实现监视,且要在**new Vue()**时就传入要监测的数据
- 如何监测数组中的数据?
- 通过包裹数组更新元素的方法实现,本质就是做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
- 通过包裹数组更新元素的方法实现,本质就是做了两件事
- 在Vue修改数组中的某个元素一定要用如下方法
- push() pop() unshift() shift() splice() sort() reverse()这几个方法被Vue重写了Vue.set()或vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm或vm的根数据对象(data等)添加属性
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>Vue检测数据改变的原理</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><!--原理:1.vue会监视data中所有层次的数据2.如何监测对象中的数据?通过setter实现监视,且要在new Vue()时就传入要监测的数据(1).对象创建后追加的属性,Vue默认不做响应式处理(2).如需给后添加的属性做响应式,请使用如下API:Vue.set(target, propertyName/index, value) 或vm.$set(target, propertyName/index, value)3.如何监测数组中的数据?通过包裹数组更新元素的方法实现,本质就是做了两件事:(1).调用原生对应的方法对数组进行更新(2).重新解析模板,进而更新页面4.在Vue修改数组中的某个元素一定要用如下方法:1.push() pop() unshift() shift() splice() sort() reverse()2.Vue.set()或vm.$set()特别注意:Vue.set() 和 vm.$set() **不能给vm或vm的根数据对象(data等)添加属性--><!-- 准备好一个容器 --><div id="root"><h1>学生信息</h1><button @click="student.age.sage++">对外年龄+1岁</button> <br /><button @click="addSex">添加性别属性,默认值:男</button> <br /><button @click="student.sex = '女' ">修改性别</button> <br /><button @click="addFriend">在列表首位添加一个朋友</button> <br /><button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br /><button @click="addHobby">添加一个爱好</button> <br /><button @click="updateHobby">修改第一个爱好为:开车</button> <br /><button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br /><h2>学校名称: {{name}}</h2><h2>学校地址: {{address}}</h2><hr/><h2>学生姓名:{{student.name}}</h2><h2>学生性别:{{student.sex}}</h2><h2>学生年龄:真实{{student.age.rage}}, 对外{{student.age.sage}}</h2><h3>爱好:</h3><ul> <li v-for="(h,index) in student.hobby" :key="index">{{ h }} </li> </ul><h2>朋友们</h2><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}} -- {{f.age}}</li></ul></div><script type="text/javascript">// 阻止vue在启动时生成生产提示Vue.config.productionTip = falseconst vm = new Vue({ el: '#root', data:{name: '尚硅谷', address: '北京',student: {name: 'tom',age: {rage: 40,sage: 29},hobby: ['抽烟', '喝酒', '烫头'],friends: [{name: 'jerry', age: 35},{name: 'tony', age: 36}]}},methods: {addSex() { // Vue.set(this.student,'sex','男') this.$set(this.student, 'sex', '男');},addFriend() { this.student.friends.unshift({ name: 'jack', age: 70 }); },updateFirstFriendName() { this.student.friends[0].name = '张三';},addHobby() { this.student.hobby.push('学习'); },updateHobby() { // this.student.hobby.splice(0,1,'开车'); // Vue.set(this.student.hobby,0,'开车'); this.$set(this.student.hobby, 0, '开车'); },removeSmoke() { this.student.hobby = this.student.hobby.filter((h) => { return h !== '抽烟'; }) }}});</script>
</body>
</html>