Vue 2.0的数据依赖实现原理代码简析(3)

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 // dep记录了和这个value值的相关依赖 this.dep = new Dep() this.vmCount = 0 // value其实就是vm._data, 即在vm._data上添加__ob__属性 def(value, '__ob__', this) // 如果是数组 if (Array.isArray(value)) { // 首先判断是否能使用__proto__属性 const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) // 遍历数组,并将obj类型的属性改为getter/setter实现 this.observeArray(value) } else { // 遍历obj上的属性,将每个属性改为getter/setter实现 this.walk(value) } } /** * Walk through each property and convert them into * getter/setters. This method should only be called when * value type is Object. */ // 将每个property对应的属性都转化为getter/setters,只能是当这个value的类型为Object时 walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** * Observe a list of Array items. */ // 监听array中的item observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }

walk方法里面调用defineReactive方法:通过遍历这个object的key,并将对应的value转化为getter/setter形式,通过闭包维护一个dep,在getter方法当中定义了这个key是如何进行依赖的收集,在setter方法中定义了当这个key对应的值改变后,如何完成相关依赖数据的更新。但是从源码当中,我们却发现当getter函数被调用的时候并非就一定会完成依赖的收集,其中还有一层判断,就是Dep.target是否存在。

/** * Define a reactive property on an Object. */ export function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function ) { // 每个属性新建一个dep实例,管理这个属性的依赖 const dep = new Dep() // 或者属性描述符 const property = Object.getOwnPropertyDescriptor(obj, key) // 如果这个属性是不可配的,即无法更改 if (property && property.configurable === false) { return } // cater for pre-defined getter/setters const getter = property && property.get const setter = property && property.set // 递归去将val转化为getter/setter // childOb将子属性也转化为Observer let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, // 定义getter -->> reactiveGetter get: function reactiveGetter () { const value = getter ? getter.call(obj) : val // 定义相应的依赖 if (Dep.target) { // Dep.target.addDep(this) // 即添加watch函数 // dep.depend()及调用了dep.addSub()只不过中间需要判断是否这个id的dep已经被包含在内了 dep.depend() // childOb也添加依赖 if (childOb) { childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } } return value }, // 定义setter -->> reactiveSetter set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } if (setter) { setter.call(obj, newVal) } else { val = newVal } // 对得到的新值进行observe childOb = observe(newVal) // 相应的依赖进行更新 dep.notify() } }) }

在上文中提到了Dep类是链接观察者和订阅者的桥梁。同时在Dep的实现当中还有一个非常重要的属性就是Dep.target,它事实就上就是一个订阅者,只有当Dep.target(订阅者)存在的时候,调用属性的getter函数的时候才能完成依赖的收集工作。

Dep.target = null const targetStack = [] export function pushTarget (_target: Watcher) { if (Dep.target) targetStack.push(Dep.target) Dep.target = _target } export function popTarget () { Dep.target = targetStack.pop() }

那么Vue是如何来实现订阅者的呢?Vue里面定义了一个类: Watcher,在Vue的整个生命周期当中,会有4类地方会实例化Watcher:

Vue实例化的过程中有watch选项

Vue实例化的过程中有computed计算属性选项

Vue原型上有挂载$watch方法: Vue.prototype.$watch,可以直接通过实例调用this.$watch方法

Vue生成了render函数,更新视图时

constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: Object ) { // 缓存这个实例vm this.vm = vm // vm实例中的_watchers中添加这个watcher vm._watchers.push(this) // options if (options) { this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy this.sync = !!options.sync } else { this.deep = this.user = this.lazy = this.sync = false } this.cb = cb this.id = ++uid // uid for batching this.active = true this.dirty = this.lazy // for lazy watchers .... // parse expression for getter if (typeof expOrFn === 'function') { this.getter = expOrFn } else { this.getter = parsePath(expOrFn) if (!this.getter) { this.getter = function () {} } } // 通过get方法去获取最新的值 // 如果lazy为true, 初始化的时候为undefined this.value = this.lazy ? undefined : this.get() } get () {...} addDep () {...} update () {...} run () {...} evaluate () {...} run () {...}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyfwzg.html