Observer类 Observer类,附着到每个被观察的对象。
一旦被添加,observer会将目标对象的属性转换为getter/setters形式,其dep用于收集依赖和发出更新通知。
它包括了对以下数据的处理:
基本类型,数组,对每一个成员加入观察。同时在数组的原型上,对数组的部分原生方法进行修改(push,pop等),从而让它们在调用时发出更新通知。
对象,直接对键进行遍历并绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 export let shouldObserve: boolean = true export function toggleObserving (value: boolean ) { shouldObserve = value } 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 ) if (Array .isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this .observeArray(value) } else { this .walk(value) } } walk(obj: Object ) { const keys = Object .keys(obj) for (let i = 0 ; i < keys.length; i++) { defineReactive(obj, keys[i]) } } observeArray(items: Array <any >) { for (let i = 0 , l = items.length; i < l; i++) { observe(items[i]) } } } function protoAugment (target, src: Object , keys: any ) { target.__proto__ = src } function copyAugment (target: Object , src: Object , keys: Array <string > ) { for (let i = 0 , l = keys.length; i < l; i++) { const key = keys[i] def(target, key, src[key]) } }
Observe函数尝试给一个对象创建observer实例,挂载在vm.__ob__。如果成功创建,返回新的observer,如果该对象已经有observer,返回原observer。
创建的条件:
必须是普通对象或Array,可以添加新属性(没有被Object.preventExtension()处理过)
shouldObserve为true
不是服务端渲染(SSR)
不是Vue实例(Vue实例要结合instance的创建过程去达到响应式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export function observe (value: any , asRootData: ?boolean ): Observer | void { if (!isObject(value) || value instanceof VNode) { return } let ob: 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 }
defineReactive函数用于为对象定义一个响应式属性。这个被定义的属性会获得一个内置的Dep,用于收集它的依赖,并发布它的更新通知。
Observer中value的每个属性,以及Vue.set()添加的新属性都会经历它的改造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 export function defineReactive (obj: Object , key: string , val: any , customSetter?: ?Function , shallow?: boolean ) { const dep = new Dep() const property = Object .getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false ) { return } const getter = property && property.get const setter = property && property.set if ((!getter || setter) && arguments .length === 2 ) { val = obj[key] } 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 if (newVal === value || (newVal !== newVal && value !== value)) { return } if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() }, }) }
Vue.prototype.set() Vue.set和vm.$set的实现,在target上设置一个响应式的属性。
如果是新增的属性,使用defineReactive()来执行添加,并立即触发一次通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 export function set (target: Array <any > | Object , key: any , val: any ): any { if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target))) { warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)} ` ) } if (Array .isArray(target) && isValidArrayIndex(key)) { target.length = Math .max(target.length, key) target.splice(key, 1 , val) return val } if (key in target && !(key in Object .prototype)) { target[key] = val return val } const ob = (target: any ).__ob__ if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' , ) return val } if (!ob) { target[key] = val return val } defineReactive(ob.value, key, val) ob.dep.notify() return val }
Vue.prototype.delete Vue.delete和vm.$delete的实现。删除某个属性,并且触发更新通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 export function del (target: Array <any > | Object , key: any ) { if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target)) ) { warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)} ` ) } if (Array .isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1 ) return } const ob = (target: any ).__ob__ if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid deleting properties on a Vue instance or its root $data ' + '- just set it to null.' , ) return } if (!hasOwn(target, key)) { return } delete target[key] if (!ob) { return } ob.dep.notify() }