文章目录
- 监测数据改变的原理之对象
- vue.set的使用
- 监测数据改变的原理之数组
Vue学习目录
上一篇:(十二)Vue之列表渲染
先看一个需求:使用列表渲染出一组数据,然后点击按钮更新其中一个信息
<!--准备好一个容器-->
<div id="root">
<!--第一步:收集用户输入的数据-->
<button @click="updateMei">更新马冬梅的信息</button>
<ul>
<li v-for="(person,index) in persons" :key="person.id">{{person.name}}-{{person.age}}-{{person.sex}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false//阻止 vue 在启动时生成生产提示。
//第二步:使用监视属性或者计算属性进行过滤
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'马冬梅',age:22,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:20,sex:'男'}
]
},
methods:{
updateMei(){
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'}
}
}
});
</script>
效果:无论怎么点按钮,数据都不会改变,打开Vue开发者工具,发现数据改变了,但是页面没有改变,不奏效,vue没有承认你这次修改。
如果把更新的逻辑改成如下:
updateMei(){
this.persons[0].name = '马老师'
this.persons[0].age = 50
this.persons[0].sex = '男'
}
效果:点击按钮,页面数据改变,Vue开发者工具也改变
监测数据改变的原理之对象
之前说过vm里面的_data存储了data数据,但可不是直接存储,vue会对data里面的数据做一些加工,我们打开发现里面有setter、getter,之前说过不是数据代理,而是数据劫持,你只要一改data数据,setter就会被调用,而且这个setter是一个响应式的setter,响应式的setter就是这个setter里面调了一个方法,这个方法会引起模板的解析。
我们把这种一旦修改data里的数据,就会被setter拦截到,或者获取数据时,会被getter拦截到,叫做数据劫持
那是怎么实现的呢,我们可以模拟一个数据监测。
数据监测步骤:数据监测也是通过Object.defineProperty进行实现的
- 1.创建一个监视的实例对象,用于监视数据中属性的变化
- 2.汇总对象中所有的属性形成一个数组
- 3.遍历,为其添加getter和setter
- 4.准备一个实例对象,把数据和监视对象赋给它
<script type="text/javascript">
let data = {
name:'一中',
addr:'广州',
}
//1.创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
//4.准备一个vm实例对象
let vm = {}
vm._data = data = obs
function Observer(obj){
//2.汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//3.遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
console.log('get被调用')
return obj[k]
},
set(val){
console.log(`${k}被改了,去解析模板,生成虚拟DOM.....`)
obj[k] = val
}
})
})
}
</script>
效果:不管访问/修改data的数据,还是访问/修改vm里_data的数据,都能监测得到。
当然我们模拟的监测也是有局限性的,如果写了一个对象,我们当前程序就废了,因为我们只考虑了一层,vue是用递归实现,它会一直往下找,直到找到不是对象为止。vue做得更好的是哪怕是一个数组对象,也能找到最低层。
vue.set的使用
我们想在data外面给data里面的对象添加属性,如果直接进行添加的话,是没有setter和getter,也就是说,vue不会对这个属性进行监测,只是当作普通的一个属性,也没有响应式,在页面不会进行展示。
在vue可以使用vue.set进行添加属性:
Vue里的set,会自动添加setter和getter,function set (target, key, val);
三个参数:
- target:要添加的对象
- key:添加的属性名
- val:添加的属性值
vm上与Vue里的set一样效果
vm.$set()
注意:Vue.set()
和 vm.\$set()
不能给vm 或 vm的根数据对象 添加属性!!!
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{name}}</h2>
<h2>学校名称:{{addr}}</h2>
<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="(friend,index) in student.friends" :key="index">
{{friend.name}}--{{friend.age}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false//阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'一中',
addr:'广州',
student:{
name:'tom',
age:{
rAge:40,//真实年龄
sAge:29,//对外年龄
},
friends:[
{name:'jack',age:35},
{name:'tony',age:34}
]
}
},
methods:{
addSex(){
//直接添加没有响应式
//this.student.sex = '男'
//Vue.set
//Vue.set(this.student,'sex','男')
//vm.$set()
this.$set(this.student,'sex','男')
}
}
});
效果:点击按钮,添加了属性,在vm身上也有setter和getter进行页面的响应
监测数据改变的原理之数组
上面程序基础上添加一个爱好属性,并进行展示
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
data:{
name:'一中',
addr:'广州',
student:{
name:'tom',
age:{
rAge:40,//真实年龄
sAge:29,//对外年龄
},
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jack',age:35},
{name:'tony',age:34}
]
}
},
效果:
去控制台查看vm中的hobby和根friends,发现没有setter和getter,而且通过最上面的程序我们知道直接改数组的数据,不会导致页面的更新。
那vue是怎么对数组做监测的呢?
vue是通过这些API得知数组的变更:
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
一旦对数组使用了上面的API,vue就会做响应式处理。
那vue怎么得知你使用上面这些API呢?
vue是对这些API进行了包装(装饰器模式),形成了自己的API,如果你通过数组调用这些方法,实际上是调用vue自己写的API。
我们查看hobby和根friends的Prototype,发现了vue对这些API进行了包装
vue对这些方法做了两件事:
- 1.调用数组原型对象对应的方法。
- 2.进行页面的响应。
数组中的某个元素还可以使用Vue.set()或vm.$set(),也会导致页面数据更新。
注意:filter()、concat() 和 slice()等等。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组