一、Object的变化侦测
vue通过Object.definProperty将对象的key转换成getter/setter的形式来追踪变化,但是getter/setter只能追踪数据是否被修改,无法追踪新增属性和删除属性。
为了解决这个问题,vue也提供了两个API:
- vm.$set
- vm.$delete
如下面这个demo:
<template>
<div>
<button @click="action">objAddName</button>
<button @click="deleteBarName">deleteBarName</button>
<p v-if="obj.name">{{ obj.name }}</p>
<p>{{ bar.name }}</p>
</div>
</template>
<script>
export default {
name: '',
data() {
return {
obj: {},
bar: {
name: 'this is bar.name'
}
}
},
methods: {
action() {
this.obj.name = 'obj.name'; // 该操作不会被侦测到
},
deleteBarName() {
delete this.bar.name // 该操作不会被侦测到
}
}
}
</script>
读取数据的时候,getter会被触发,然后进行依赖收集,收集了依赖之后还需要将依赖存储起来,以备使用。
当数据发生变化的时候,触发setter,然后循环依赖列表,把所有的watcher都通知一遍。
二、Array的变化侦测
Array的变化侦测和Object不一样,Array是通过数组的方法来改变内容的,所以对Array的变化侦测是采用创建拦截器去覆盖原型中的方法的形式来追踪变化。
Array收集依赖的方式和Object一样,都是在getter中收集,但是依赖的存储位置是不同的;因为数组要在拦截器中向依赖发消息,所以不能像Object那样保存在definReactive中,而是把依赖保存在了Observer实例上。
但也因为Array的变化侦测方式上的原因,有一些方法导致数组发生变化也是无法侦测到的,如:
this.arr[0] = 1;
this.arr.length = 0;
像这样的操作,并不是通过数组的方法来改变数组本身,是不会被vue侦测到的。
数组的方法中,有7个方法可以改变数组本身:
- push
- pop
- unshift
- shift
- splice
- sort
- reverse
上面这7个方法都是被新创建的拦截器所拦截了的。