这是我关于VUE的列表渲染的学习笔记如下:
第一步、我们先初步认识
我们要实现列表渲染就要借助于v-for指令来实现。
其使用方法:
1.这个指令可以遍历数据进行渲染列表进而展示数据。
2.其使用格式:语法:v-for=“(item, index) in items” :key=“item.message"或者v-for=”(item, index) of items" :key=“item.message”
3.v-for可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
代码案例如下:
<!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>列表渲染(基本使用)</title>
<script type="text/javascript" src="../vue_js/vue.js"></script>
<style>
li {
list-style: none;
}
</style>
</head>
<body>
<!--用 v-for 把一个数组对应为一组元素-->
<div id="root">
<h1>遍历数组</h1>
<ul>
<li v-for="(c,i) of city" :key="i">
{{c}}------{{c.id}}------{{c.cityName}}
</li>
</ul>
<h1>遍历对象</h1>
<ul>
<li v-for="(v,k) of person" :key="k">
{{v}}-----{{k}}
</li>
</ul>
<h1>遍历字符串</h1>
<ul>
<li v-for="(v,k) of str" :key="k">
{{v}}-----{{k}}
</li>
</ul>
<h2>测试遍历指定次数(用得少)</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
index:{{index}}----value:{{number}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
city: [
{ id: '001', cityName: '张三' },
{ id: '002', cityName: '李四', },
{ id: '003', cityName: '王五', }],
person: {
eat: '饭',
eye: '一双眼睛',
ears: '一双耳朵',
face: '一张脸'
},
str: 'Chinese'
}
});
</script>
</body>
</html>
第二步、key的原理
1.虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟 DOM】的差异比较
2.比较规则如下:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ,界面效果没问题, 但效率低。
(2)如果结构中还包含输入类的DOM:会产生错误DOM产生, 界面有问题。
4. 开发中如何选择key?
(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
(2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用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>key的作用和原理</title>
<script type="text/javascript" src="../vue_js/vue.js"></script>
<style>
li {
list-style: none;
}
</style>
</head>
<body>
<h1>ul列表</h1>
<div id="demo">
<button @click.once="addFruit">向水果列表添加水果</button>
<ul>
<li v-for="(fruit,index) of fruits" :key="index">
{{fruit}}-----{{index}}:<input type="text">
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#demo',
data: {
fruits: ['苹果', '梨子', '香蕉', '橘子', '红枣', '葡萄']
},
methods: {
addFruit() {
let f = "柚子"
//往前加
this.fruits.unshift(f)
//往后加
//this.fruits.push(f)
}
},
});
</script>
</body>
</html>
没有点击按钮之前的图片:
点击按钮之后的图片:
在这里有一个明显的问题,当我们再列表的前面添加数据的时候,输入框里面的内容的位置错乱,本应该再苹果那里,但是点击之后却显示再柚子的位置上。为啥会这样呢?
列表渲染的更新主要是根据key来更新, key是虚拟DOM对象的标识,VUE会根据VUE数据的变化,产生新的虚拟DOM,接着产生新的真实DOM。新旧虚拟DOM会根据key进行比较。新数据的柚子和旧数据的苹果进行比较,发现不一样,就创建新的DOM,接着比较后面输入框,发现新旧虚拟DOM都有输入框input,所以直接使用旧的真实DOM,所以“苹果----0”就会直接服用旧的真实DOM,还是位于key=0的位置上。
第三步、列表过滤
我们可以filter函数的基础上利用watch侦听器实现或者计算属性实现,下面就是都采用了这两种方式实现的:
一般计算属性可以实现的,watch也可以实现,但是我们一般来说都会采用计算属性,计算属性一般可能更加简单一些。
<!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>列表过滤</title>
<script type="text/javascript" src="../vue_js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>使用watch侦听器实现</h1>
<input type="text" placeholder="请输入名字" v-model="keyWord1">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<h1>使用计算属性实现</h1>
<input type="text" placeholder="请输入提示信息" v-model="keyWord2">
<ul>
<li v-for="(f,index) of filPerons1" :key="index">
{{f.name}}-{{f.color}}-{{f.varieties}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
keyWord1: '',
keyWord2: '',
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: '男' }
],
fuits: [
{ id: '001', name: '香蕉', color: '黄色', varieties: '芝麻蕉' },
{ id: '002', name: '苹果', color: '青色', varieties: '红将军' },
{ id: '003', name: '大枣', color: '红色', varieties: '壶瓶枣' },
{ id: '004', name: '梨子', color: '黄色', varieties: '黄金梨' }
],
filPerons: [],
},
watch: {
keyWord1: {
immediate:true,
handler(val) {
this.filPerons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
},
computed: {
filPerons1: {
get() {
return this.fuits.filter((p) => {
return p.name.indexOf(this.keyWord2) !== -1
})
}
}
}
});
</script>
</body>
</html>
第四步、列表排序:
这里实现列表排序主要是sort实现的以及根据sortType来判断排序的方式,如顺序升序,降序排序,以及原始排序,实现排序方式的代码是“return this.sortType === 1 ? (s2.score - s1.score) : (s1.score - s2.score)”也就是一个三元表达式。
<!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>列表排序</title>
<script type="text/javascript" src="../vue_js/vue.js"></script>
<style>
li {
list-style: none;
}
</style>
</head>
<body>
<div id="root">
<input type="text" placeholder="请输入名字" v-model="searchStudent">
<button @click="sortType = 2">分数升序</button>
<button @click="sortType = 1">分数降序</button>
<button @click="sortType = 0">原始顺序</button>
<ul>
<li v-for="(v,k) of sortStudent" :key="v.id">
{{v.studentName}}----{{v.score}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
searchStudent: '',
sortType:0,
student: [
{ id: '001', studentName: '张三', score: 100 },
{ id: '002', studentName: '李四', score: 89 },
{ id: '003', studentName: '王五', score: 94 },
{ id: '004', studentName: '小明', score: 84 },
{ id: '005', studentName: '小刚', score: 99 },
{ id: '006', studentName: '小华', score: 67 },
{ id: '007', studentName: '小红', score: 81 },
{ id: '008', studentName: '小芳', score: 62 },
{ id: '009', studentName: '赵虎', score: 97 },
{ id: '010', studentName: '张龙', score: 99 },
{ id: '011', studentName: '马汉', score: 73 },
{ id: '012', studentName: '罗三', score: 92 },
{ id: '013', studentName: '刘贝', score: 91 }
]
},
computed: {
sortStudent() {
console.log(this.sortType);
const studentArr = this.student.filter((s) => {
return s.studentName.indexOf(this.searchStudent) !== -1;
});
if (this.sortType) {
studentArr.sort((s1, s2) => {
return this.sortType === 1 ? (s2.score - s1.score) : (s1.score - s2.score)
});
}
return studentArr;
}
}
});
</script>
</body>
</html>
第五步、VUE监测数据变化的原理
Vue 监视数据的原理:
1.vue 会监视 data 中所有层次的数据。
2. 如何监测对象中的数据?
通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。(如下图)
(1)对象中后追加的属性,vue 默认不做响应式处理
(2)如需给后添加的属性做响应式,请使用如下API:
Vue.set(target, propertyName/index, value) 或 vm.set()
其中的案例代码如下:
<!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>Vue的总结数据监测</title>
<script type="text/javascript" src="../vue_js/vue.js"></script>
<style>
#s {
width: 100px;
height: 100px;
background-color: rgb(72, 23, 128);
text-align: center;
font-size: 70px;
color: white;
}
</style>
</head>
<body>
<div id="root">
<button @click="num++">点击按钮实现下面的数字加一</button><br>
<div id="s">{{num}}</div><br>
<button @click="myFriend.age++">年龄+1岁</button>
<button @click="myFriend.sex = '“由于情况特殊,所以该选项位置”' ">修改性别</button> <br />
<button @click.once="addBallGame('冰球')">在球类列表再添加一个其他的球类项目(但是只能增加一次)</button>
<button @click="addFriend">在列表首位添加一个朋友</button> <br />
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br />
<button @click="updateHobby">修改第2个爱好为Basketball</button> <br />
<button @click="removeRugby">过滤掉爱好中的橄榄球</button> <br />
<h2>这是我朋友的年龄,他的名字是{{myFriend.name}},今年是{{myFriend.age}}岁。他的性别是<span
v-if="myFriend.sex">{{myFriend.sex}}</span></h2>
<h2>我这个朋友善于球类运动项目,比如下面这些:</h2>
<ul>
<li v-for="(value,index) of myFriend.habby" :key="index">
{{value}}
</li>
</ul>
<h2>他除了我之外也有很多其他朋友,如下面这些人:</h2>
<ul>
<li v-for="(hf,index) in myFriend.hisFriends" :key="index">
{{hf.name}}----{{hf.age}}岁------{{hf.sex}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
num: 0,
myFriend: {
name: '小明',
age: 18,
sex: '男',
habby: ['足球', '篮球', '排球', '羽毛球', '网球', '乒乓球', '橄榄球', '台球'],
hisFriends: [
{ name: '小华', age: 19, sex: '男' },
{ name: '小刚', age: 19, sex: '男' },
{ name: '小芳', age: 17, sex: '女' },
{ name: '小红', age: 20, sex: '女' },
{ name: '小坤', age: 18, sex: '男' }
]
}
},
methods: {
addBallGame(ball) {
this.myFriend.habby.push(ball);
},
addFriend() {
this.myFriend.hisFriends.unshift({ name: 'jack', age: 70, sex: '男' })
},
updateFirstFriendName() {
this.myFriend.hisFriends[0].name = "张三";
},
updateHobby() {
// this.student.hobby.splice(0,1,'Basketball车')
// Vue.set(this.student.hobby,1,'Basketball')
this.$set(this.myFriend.habby, 1, 'Basketball')
},
removeRugby(){
this.myFriend.habby = this.myFriend.habby.filter((obj)=>{
return obj!='橄榄球'
})
}
},
});
</script>
</body>
</html>