Observer
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor(value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
// 给value添加__ob__属性,值就是本Observer对象,value.__ob__ = this;
// Vue.$data 中每个对象都有 __ob__ 属性,包括 Vue.$data对象本身
def(value, '__ob__', this)
// 判断是否为数组,不是的话调用walk()添加getter和setter
// 如果是数组,调用observeArray()遍历数组,为数组内每个对象添加getter和setter
// 并重写push/pop/unshift/shift/splice/sort/reverse方法,当调用这些方法是,主动调用value.__ob__.dep.notify()方法
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
}
// 遍历每个属性并将它们转换为getter/setter。只有当值类型为对象时才调用此方法。
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
// 如果是数组继续执行 observe 方法, 其中会继续新建 Observer 对象, 直到穷举完毕执行 walk 方法
observe(items[i])
}
}
}
constructor中,首先将__ob__属性添加到value上,并将该属性指向Observer对象本身,然后判断value的类型,如果是Array,则调用observeArray对数组中对每个值进行处理,否则,即为Object时,直接调用walk方法,来对对象中对每个kv对进行处理。
observe
// 该方法用于观察一个对象,返回与对象相关的Observer对象,如果没有则为value创建一个对应的Observer
export function observe (value: any, asRootData: ?boolean): Observer | void {
// typeof返回值非object 或 value存在于VNode.prototype的原型链上时
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) && // 是否可扩展,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
observe中,判断value.__ob__是否存在,如果不存在,则调用new Observer,将value变成被监听对象。
defineReactive
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 获取已经实现的 getter /setter 方法
const getter = property && property.get
const setter = property && property.set
// 监听key所对应的val的子属性
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// Dep.target 全局变量指向的就是当前正在解析指令的Complie生成的 Watcher,即当前活动的Watcher实例
// 如果当前有活动的Watcher实例,
// 会执行到 dep.addSub(Dep.target), 将 Watcher 添加到 Dep 对象的 Watcher 列表中
if (Dep.target) {
// 将dep放进当前观察者的deps中,同时,将该观察者放入dep中,等待变更通知
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()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
// 如果数据被重新赋值了, 调用 Dep 的 notify 方法, 遍历dep.subs, 通知所有的 Watcher
dep.notify()
}
})
}
defineReactive中,重写了obj对getter和setter方法,当调用get时,通过dep.depend()将 Watcher 添加到当前 Dep 对象的 Watcher 列表中,并在调用set时,通过dep.notify()来通知所有依赖的Watcher进行更新,因为new Dep()其实是在闭包中调用,所以每个obj.key对应一个自身当dep。
src/core/observer/dep.js
// Dep是订阅者Watcher对应的数据依赖
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor() {
// 每个Dep都有唯一的ID
this.id = uid++
// subs用于存放依赖
this.subs = []
}
// 向dep的观察者列表subs添加观察者
addSub (sub: Watcher) {
this.subs.push(sub)
}
// 从dep的观察者列表subs移除观察者
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
// 依赖收集:如果当前有观察者,将该dep放进当前观察者的deps中
// 同时,将当前观察者放入观察者列表subs中
depend () {
// 这里添加了Dep.target是否存在的判断,目的是判断是不是Watcher的构造函数调用
// 也就是说判断他是Watcher的this.get调用的,而不是普通调用
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
// 通知所有绑定 Watcher。调用watcher的update()
subs[i].update()
}
}
}