Vue2.0中为了实现数据响应式通过使用Object.defineProperty添加getter和setter对数据变更进行了拦截,并在数据获取时收集依赖,在数据更改时进行通知对应的依赖执行更新函数。
数据的添加和删除
Object.defineProperty只能对对象进行拦截,因此Vue2.0中的响应式操作基本上是通过拦截data对象的key完成的,但是Object.defineProperty无法拦截对象属性的添加和删除,因此添加和删除响应式数据需要通过Vue.set和Vue.delete来进行通知
数据响应式初始化
在vue数据响应式的初始化过程有几个重要的类和方法Observer、Dep、Watcher、observe、defineReactive
observe
observe是一个返回响应式数据实例的方法
它会检查给定的值是否是一个对象,如果不是会直接return,否则会继续检查它是否是一个Observer实例,如果是的话直接返回这个Observer实例,否则会通过Observer创建一个实例然后返回。
简易源码展示
export function observe (value) {
if (!isObject(value)) {
return
}
let ob
if (value instanceof Observer) {
ob = value
} else {
ob = new Observer(value)
}
return ob
}
Observer
Observer是一个将对象变为响应式数据的类
Observer
- 为对象创建Dep对象
- 判断对象是不是数组,如果是数组则通过覆盖7个会改变原数组的方法来进行通知和拦截,并循环数组执行observe,对每个元素执行响应式处理;这也是为什么通过
arr[1] = 1无法触发响应式的原因- push
- pop
- shift
- unshift
- splice
- sort
- reverse
- 如果不是则遍历对象依次为每个key执行defineReactive进行对象key的拦截
defineReactive
defineReact是一个负责使用Object.defineProperty对对象的key进行拦截的函数
- 使用Dep创建dep
- 使用observe对obj[key]进行响应式处理,返回子ob
- 使用Object.defineProperty为obj的key添加setter和getter
getter
当访问key时,判断Dep.target上是否存在值,如果存在则使用dep.depend()收集依赖,如果子ob存在,则子ob也进行依赖收集,如果是数组,则循环数组进行收集
setter
当key的值改变时,判断新值和旧值是否相同,不同则替换新值,然后对新值进行响应式处理,最后调用dep.notify()通知变更
Dep
Dep是一个收集并存储依赖,通知更新的类
Dep主要分为负责数据变更和数据增删两种类型的Dep
在Observer类创建的主要负责数据的增删通知,可以称它为大管家,主要在Vue.set、Vue.delete和数组变更或对象类型增删时使用
在defineReactive中创建的则负责数据的变更通知,一般叫它小管家,主要负责基础类型的数据变更时使用
Watcher
Watcher用来触发更新,主要分为render watcher和user watcher
render watcher每个组件会产生一个,主要用来执行组件的render和update函数来更新页面
user watcher一般是用户创建的watch,它则负责执行用户的逻辑
Dep和Watcher
vue中由于Watcher分为render watcher和user watcher,render watcher的粒度为每个组件一个watcher,而每个组件中有多个dep,因此dep和watcher是多对多的关系,因此Dep和Watcher是互相持有的关系,当数据发生改变时,Dep会通知所持有的多个依赖,即watcher进行更新,而当watcher中有不需要的dep时需要通知dep移除自身,以免之后数据改变时,不需要的dep继续通知自己更新