还有value、writable、get、set,都比较好理解就不再赘述。
1.2、new Observe()
遍历完keys,就是以data作为参数调用observe
了,而observe内部得主要内容就是ob = new Observer(value)
,再看Observer这个类。(有一种抽丝剥茧得感觉)
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
}
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i defineReactive(obj, keys[i])
}
}
observeArray (items: Array) {
for (let i = 0, l = items.length; i observe(items[i])
}
}
}
函数def
的作用就是在对象上定义属性。然后判断传进的data是对象还是数组。
1.2.1、Array.isArray(value)
如果value是数组的话,先通过hasProto
这个自定义函数来判断当前环境中是否存在__proto__
,如果有的话就可以直接用,没有的话,手动
实现一下,功能是一样的,那就只看protoAugment(value, arrayMethods)
干了啥就好
function protoAugment (target, src: Object) {
target.__proto__ = src
}
其中target自然就是我们observe的数组,而src也就是arrayMethods的定义如下
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[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)
// notify change
ob.dep.notify()
return result
})
})
看着代码里的methodsToPatch
里的几项,眼熟吗
再看到倒数第四行的ob.dep.notify()
,配上官方注释notify change。
也就是说arrayMethods
是一个继承数组原型的对象,并对其中特定的几种方法做了处理,然后在new Observe(value)
的时候,如果value是数组,就让value继承这个arrayMethods
,然后这个数组调用特定的方法时,会调用当前Observe类上的dep属性的notify
方法,进行后续操作。
定义完这些,再进行递归对数组中的每一项继续调用observe
1.2.2、walk & defineReactive
然后对于对象而言,直接调用walk
,然后遍历对象中的非继承属性,对每一项调用defineReactive
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
defineReactive
的主要代码就是各种判断递归和Object.defineProperty()
了,这也是双向绑定的关键一部分,从数据到DOM。
其中对get的定义包含了if(Dep.target){ dep.depend() }
,对set的定义包含了dep.notify()
,接下来看Dep的方法。
1.3 Dep
Dep的定义是这样的
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i subs[i].update()
}
}
}
来看在get中调用的dep.depend()
,Dep.target
不为空的情况下,以this为参数,调用Dep.target.addDep
,target是Dep类的静态属性,类型为Watcher,方法addDep定义如下
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)
}
}
}
可以看到addDep有去重dep的作用,然后通过调用dep.addSub(this)
,把当前的Dep.target push到subs中。
也就是说,data里面有个observer,然后observer里面有个dep,dep里面有个watcher数组,收集依赖一条龙。
至于在set中调用的dep.notify()
,是遍历watcher数组,调用每一项的update方法,而update方法,核心代码是调用watcher的run方法,run方法的核心是this.cb.call(this.vm, value, oldValue)
。问题又来了,这个cb是new Watcher时的传参,但是从initState
一步一步看下来,先new一个Observe,然后定义其中每个属性的get
和set
,get
时收集依赖,set
时通知变更。但是并没有看到哪里真的触发了我们所设置的get
,而且之前说到的Dep.target
是个啥呢。