本文已参与「新人创作礼」活动,一起开启掘金创作之路。
需要您明白大致的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