07-VUE监测的原理
1.前言
当我们在Vue实例中写了一个对象数组在data属性中,当我们想修改对象数组中的一个对象中的属性值时,我们可以通过一个一个修改对象属性值实现
<div id="root">
<h1>人员列表</h1>
<button @click="updateMei">更新马冬梅的信息</button>
<ul v-for="(p,index) in persons" :key="p.id">
<li>
{{p.name}}-{{p.age}}-{{p.gender}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip=false;
const vm=new Vue({
el:'#root',
data:{
persons:[
{id:1,name:"马冬梅",age:48,gender:"女"},
{id:2,name:"周冬雨",age:28,gender:"女"},
{id:3,name:"周杰伦",age:38,gender:"男"},
{id:4,name:"温兆伦",age:18,gender:"男"},
],
},
methods: {
updateMei(){
this.persons[0].name='马老师',//奏效
this.persons[0].age=69,//奏效
this.persons[0].gender='男'//奏效
}
},
})
</script>
可以看出一个一个修改对象属性值时可以实现修改对象属性值的要求的,且是响应式的,数据会实时更新,但是如果我们通过对单个对象数组元素整体重新赋值来达到修改对象属性值的目的,就会发现数据不会实时更新在页面上
methods: {
updateMei(){
this.persons[0]={id:1,name:"马老师",age:69,gender:"男"}//不奏效
}
},
也就说明当我们通过对单个对象数组元素整体重新赋值来达到修改对象属性值的目的是是行不通的,VUE监测不到数据的变更,无法实现数据响应重新渲染数据在页面上,这就需要我们理解VUE监测的原理
2.如何监测对象中的数据?
对于对象的修改我们可以直接通过重新赋值实现,且为响应式,数据会重新渲染在页面上
打开控制,输出vm中的_data属性
也是说VUE对于每一个对象属性都配置了get和set函数,其中的set函数不仅实现了对象属性值的修改,还通过某种方式实现了重新渲染数据在页面上
我们来模拟实现一个set函数
let data={
name:"海绵宝宝",
address:"比奇堡"
}
// 创建一个检测的实例对象,用于监视data中的属性的变化
const obs=new Observer(data)
console.log(obs);
// 准备一个vm实例对象
let vm={}
vm._data=data=obs
function Observer(obj) {
// 汇总对象中所有属性形成一个数组
const keys=Object.keys(obj)
// 遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被改了,`);
obj[k]=val
}
})
})
}
3.Vue.ser()方法
VUE所写的set函数比我们模拟的set函数写的更加复杂完备
我们使用VUE的API自备的set函数来修改对象属性的值
例:Vue.set(vm._data.student,‘sex’,‘男’)
vm.$set(vm._data.student,‘sex’,‘男’)
<div id="root">
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性,默认值为男</button>
<h2>学生姓名:{{student.name}}</h2>
<h2 v-if="student.sex">学生性别:{{student.sex}}</h2>
<h2>学生年龄:{{student.age.rAge}},对外{{student.age.sAge}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="f in student.friends">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:{
rAge:40,
sAge:29,
},
friends:[
{name:'jerry',age:35},
{name:'mary',age:40},
]
}
},
methods: {
addSex(){
// Vue.set(vm._data.student,'sex','男')//vue的API自带的set方法
Vue.set(this.student,'sex','男')
}
},
})
</script>
注意:不能给vm或vm的根数据对象添加属性
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<h2 v-if="leader">学生校长:{{leader}}</h2>
</div>
4.如何监测数组中的数据?
回到开头的问题,在VUE中我们不能直接通过对对象数组中某个元素的重新赋值来达到修改对象属性值的目的
因为VUE没有给数组中的元素分配跟对象属性一样的set函数,所以当使用重新赋值的方式修改属性值时VUE并不能监测到对象属性值的的变化也不会重新渲染数据
对于修改数组中元素的值,我们也可以使用Vue.set()方法来实现
<div id="root">
<h2>爱好</h2>
<ul>
<li v-for="h in student.hobby">
{{h}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip=false;
const vm=new Vue({
el:'#root',
data:{
hobby:['抽烟','喝酒','烫头']
}
},
methods: {
addSex(){
Vue.set(this.student.hobby,1,'打游戏')
}
},
})
</script>
除了使用Vue.set()方法和vm.$set()方法,我们还可以使用原生JS中数组相关的重构数组方法,比如push()、pop()、shift()、unshift()、splice()、sort()、reverse()
5.VUE监测原理的总结
Vue监视数据的原理:
1.vue会监视data中所有层次的数据。
2.如何监测对象中的数据?
通过setter实现监视,且要在new Vuel时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
3.如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
(1).使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse(),如果使用filter()这类会产生新数组的数组方法,如果创建一个新数组来接收方法新产生的新数组
(2).Vue.set()或vm.$set()
特别注意:Vue.set()和vm.$set()不能给vm或m的根数据对象添加属性!!!
练习
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br/>
<button @click="addGender">添加一个性别属性,默认值为男</button><br/>
<button @click="student.gender='未知'">修改性别属性</button><br/>
<button @click="addFriends">在列表首位添加一个朋友</button><br/>
<button @click="updataFirstFriendsName">修改第一个朋友的名字为:张三</button><br/>
<button @click="addHobby">添加一个爱好</button><br/>
<button @click="updateHobby">修改第一个爱好为:开车</button><br/>
<button @click="removeSmoke('抽烟')">过滤掉抽烟的爱好</button><br/>
<hr>
<h2>学生姓名:{{student.name}}</h2>
<h2>学生年龄:{{student.age}}</h2>
<h2 v-if="student.gender">学生性别:{{student.gender}}</h2>
<h2>爱好:</h2>
<ul>
<li v-for="h in student.hobby">
{{h}}
</li>
</ul>
<h2>朋友们:</h2>
<ul>
<li v-for="f in student.friends">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip=false;
const vm=new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:49,
friends:[
{name:'jerry',age:35},
{name:'mary',age:40},
],
hobby:['抽烟','喝酒','烫头']
}
},
methods: {
addGender(){
// Vue.set(this.student,'gender','男')
this.$set(this.student,'gender','男')
},
addFriends(){
this.student.friends.unshift({name:'Aay',age:18})
},
updataFirstFriendsName(){
this.student.friends[0].name='张三' //name有专属服务的set方法
},
addHobby(){
this.student.hobby.push('打游戏')
},
updateHobby(){
// Vue.set(this.student.hobby,0,'开车')
// this.$set(this.student.hobby,0,'开车')
this.student.hobby.splice(0,1,'开车')
},
removeSmoke(value){
this.student.hobby=this.student.hobby.filter((h)=>{
return h!=value
})
}
},
})
</script>