需要您明白大致的Vue响应式原理相关知识(Observer
、Dep
、Watcher
)
问题:
在Vue
响应式的class Observer
部分,我们发现会有两个部分都创建了Dep
实例
export class Observer {
constructor (value: any) {
this.value = value
this.dep = new Dep() // 第一个创建的dep----------------------------------
this.vmCount = 0
def(value, '__ob__', this)
...
}
...
}
...
export function defineReactive (obj, key, val, customSetter, shallow) {
const dep = new Dep() // 第二个创建的dep----------------------------------
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
...
}
那么这两个dep到底都是干什么用的呢?
分别解答:
defineReactive - dep
我们先说defineReactive
中的dep
,这个dep
是闭包在defineReactive
函数中的,无法直接从data
的属性中直接捕获到该dep
,他的订阅/触发位置是在Object.defineProperty
中的get
/set
里触发的
这个实例的作用很明显,是用于对象增加/删除属性时而被调用的
Observer - dep
这个dep
在Observer
创建一开始就被实例化,而且他和上边的dep
不同,他被绑定在Observer
的实例上,也就是在外界同样可以取到他
这个dep
大多情况下是给数组使用,具体使用地方见下方:
- 数组更新的watcher收集
以一个例子说明:
new Vue({
data() {
return {
arr: [1,2,3,4],
a: 'xxx',
b: ...
...
}
},
...
})
在初始化阶段,data
中的所有数据都会被递归进行响应式代理,data
中数组的数据也会被 option.data
(用户传的data)本身也会被代理
**在渲染阶段,获取data中的数组(arr:[...]
)时,**会执行Object.defineProperty(data,'arr',fun)
所代理的get
方法,在其中会调用childOb.dep.depend()
方法,对该dep
收集依赖
此处说明childOb.dep.depend()
方法位置
function defineReactive (...) {
const dep = new Dep()
...
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// 提示:get中的内容是在运行阶段执行的,创建Observer、dep实例是在渲染时就已经生成的
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
// ↓就是这句话,此处的childOb指的就是arr的Observer实例
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
}
- 数组的修改
数组是没有Object.defineProperty的,Vue中修改数组数据,采用的是代理数组中的方法(push
,pop
,shift
,unshift
,splice
,sort
,reverse
)来达到响应式的目的,而在这7个Vue
代理过的函数中,通知watcher
更新就是调用的这个dep
// 截取代码片段
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']
methodsToPatch.forEach(function (method) {
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// ↓这里的更新就是调用的数组的Observer -> dep中的notify
ob.dep.notify()
return result
})
})
- set方法/delete方法
使用set、delete方法为对象中赋值新的属性时,调用的ob.dep.notify()
就是target
的Observer - dep.notify