前言
我还是感觉看了个寂寞, 一直分析代码执行, 但是忽略了比较宏观的一些事, 比如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`通知.
|