响应式原理分析
-
原理流程图概览
image.png
-
响应式相关的源码在(源码位于instance/index.js) stateMixin
-
stateMixin中initData实现两个功能
-
将data中的对象代理(proxy)到_data上
export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) } // 也就是说vm._data.变量都是响应式数据(即vm.变量)。
-
将data中的数据变为响应式数据,即
// observe data observe(data, true /* asRootData */)
-
observe类 (上一步中的observer函数)
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 ) { // 将value变成响应式数据 ob = new Observer(value) } if (asRootData && ob) { ob.vmCount++ } return ob }
-
Observer的构造函数&walk函数
constructor (value: any) { this.value = value // 每一个响应式对象都有一个Dep去收集依赖watcher 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 through each property and convert them into * getter/setters. This method should only be called when * value type is Object. */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } }
-
上面的defineReactive方法将数据变为响应式,核心代码:
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() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) // 当数据变化时触发更新。 dep.notify() }
-
-
Dep类
// 可以理解为观察者模式中的发布者 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) } // 移除依赖 removeSub (sub: Watcher) { remove(this.subs, sub) } depend () { if (Dep.target) { // 在watcher中添加dep依赖 Dep.target.addDep(this) } } // 提醒(data, computed, watch)各类型的watcher , 进行更新 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 < l; i++) { subs[i].update() } } }
Dep类的构造函数中的subs是Watcher(观察者)类。vue实例中data的一个值,可以添加多个Watcher,同时这个值变化的时候也是触发这多个Watcher的更新。
-
Watcher类
- Watcher类主要用来收集依赖和触发更新。
- Watcher类也是实现了$watch(),即:https://cn.vuejs.org/v2/api/#watch
-
Observer、Dep和Watcher类关系
image.png
Observer类是发布者(主题)(vue实例的data对象),里面有好多订阅者(Dep类),每本主题可以被订阅(Watcher类)。
当某主题更新的时候,订阅的Watcher类会收到通知,进而更新订阅者就会收到消息然后进行自我更新(vue实例的data对象)。
Observer类, 给data中的属性分配Dep, 一个Dep可以有多个类型的依赖watcher (如data,computed,watch)
网友评论