前言
我还是感觉看了个寂寞, 一直分析代码执行, 但是忽略了比较宏观的一些事, 比如Dep 如何将变化通知到Watcher , Watcher 是怎么订阅Dep 的, 缺乏这种意识.
一、由initData整理思路
1.1 initData()到observe()
从vm 提取data 数据对象, 传给observe
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
observe(data, true)
}
1.2 observe()
检查该data 数据对象是否已受观察, 即检查该数据对象内是否具备__ob__ 属性, 若有则说明已受观察不做处理, 若无则设置观察者new Observer(value) . 可能因为是初始化, 所以只对没有被观察的设置观察.
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
1.3 new Observer()
将Dep , 数据对象, vmConunt 传入def()
export class Observer {
value: any;
dep: Dep;
vmCount: number;
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
this.walk(value)
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
}
}
1.4 def()
核心defineProperty() 为数据对象value 设置__ob__ 属性, 至此该对象被Dep劫持 Object.defineProperty(value, '__ob__', { Dep, 数据对象, vmCount })
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
完成后value 上具备__ob__ 属性.
{
xxx: 'xxx',
x: 'xxx',
__ob__: {
dep: {
...
subs: [
订阅者0,
订阅者1,
...
]
}
}
}
此时Observer 又调用了walk(value)
1.5 walk()
def 完成后value 会带着Dep 来执行walk() 提取数据对象的key构成数组, 遍历数组对其内部元素defineReactive(数据对象, 属性名) 将数据对象中每个属性都执行defineReactive() .
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
1.6 defineReactive()
defineReactive(数据对象, 属性名) , 内部Object.property(数据对象, 属性名, {..., get, set}) 为数据对象设置get 和set 方法, 在初始化的时候会触发get , 后期更新数据会触发set .
get 即初始化时检查Dep.target , 这个变量就是订阅该Dep 的一个Watcher , new Watcher() 时会触发Watcher 类的get() 执行dep 的pushTarget(this) , 这个this 指向一个Watcher , 随后pushTarget() 内Dep.target = 这个Watcher , 从而检索成功放行.
放行后执行dep.depend() 调用该dep 的depend() 方法进而将这个dep 加入到Dep.Target对应的Watcher 的newDeps 数组内, 表示这是该Watcher 订阅的Dep , 同时Dep 也会调用addSub() 将这个Watcher 加入自己的Subs 数组表示这是自己的订阅者. 每个属性对应一个负责劫持的Observer , 每个Observer 通知一个Dep , 一个Dep 可以由多个Watcher 订阅, Dep 也通知变化到多个Watcher .
数据更新时会触发set 进而调用Dep 的notify() 通知函数, 通知订阅该Dep 的所有Watcher 执行update() , 下面会说到.
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
dep.notify()
}
})
}
1.7 new Dep() && depend()
defineReactive() 中defineProperty() 定义的get() 内部由Observer 实例调用Dep 的depend() , 在depend() 会调用Watcher 类的addDep() 方法, Watcher 借此拿到Dep . addDep 还调用Dep 的addSub() , Dep 借此拿到Watcher , 此时双方订阅与被订阅关系构成. Dep 的Subs 数组用来存放所有订阅者Watcher .
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
1.7.1 pushTarget()
Dep.target = null
const targetStack = []
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
1.8 Watcher
Watcher 实例化先调用get() 里的pushTarget() (这个函数也在dep.js中但不属于Dep中)传自己过去, 然后pushTarget 将Watcher 赋值给Dep.target , 此时defineReactive() 里的dep.depend() 能够执行. 直到newDep() 发生, Dep 的depend(this) 执行Dep.target.addDep() (addDep() 是Watcher 的方法, 两者借此建立联系), 双方各自把对方加进自己的数组newDep 和Sub , 订阅算是完成.
export default class Watcher {
get () {
pushTarget(this)
}
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
update () {
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
run () {
if (this.active) {
const value = this.get()
}
}
}
1.9 initData图示

二、由initComputed整理思路
2.1 由initComputed()到defineComputed()
整个主体在一个loop完成, 为每个计算属性实例化Watcher , vm._watchers.push(this) 将该Watcher存入vm的诸多Watcher 中. 然后对每个计算属性执行defineComputed(vm, 计算属性名, 计算属性值)
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
function initComputed (vm: Component, computed: Object) {
const watchers = vm._computedWatchers = Object.create(null)
const isSSR = isServerRendering()
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (!isSSR) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
}
}
}
2.2 defineComputed()
针对computed 属性值的不同书写方式组成sharedPropertyDefinition 作为该计算属性的描述符参数.
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
2.3 createComputedGetter()
在上一步为每个计算属性实例化了Watcher , 拿到当前计算属性的Watcher , 执行Watcher 的evaluate() 调用Watcher 的get() 和Dep的pushTarget , 导致Dep.target = 当前Watcher Dep.target 存在后由Watcher 调用Dep的depend() , 然后就回到1.8的addDep() 订阅了.
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
2.4 initComputed图示

总结
每次到addDep() , 也就是每次Watcher 调用Dep 的depend() , 都会进行双向数据收集, Watcher和Dep互相同步数据, Dep的数据更新后通知订阅自己的每个Watcher调用notify() 进而调用update() 更新数据, computed计算属性在接受 defineProperty()之后会拥有 set()和 get()方法, 更新调 set()之后 Dep`通知.
|