Vue 源码浅析:数据动态响应-观察者对象 Observer


Vue 源码浅析:数据动态响应-观察者对象 Observer

继续初始化中未完的内容

初始化流程中,还未提到 initState,这个方法涉及 vue 中有关数据状态的一些操作:

<code>export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}/<code>

initState 方法内涉及 Vue 主要几个 options 属性的初始化,他们具备数据动态响应特性,我们大致看下他们的方法的内部结构:

initProps

<code>function initProps (vm: Component, propsOptions: Object) {
//...
defineReactive(props, key, value)
}/<code>

initData

<code>function initData (vm: Component) { 

//...
observe(data, true /* asRootData */)
}/<code>

initComputed

<code>function initComputed (vm: Component, computed: Object) {
//...
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}/<code>

initWatch

<code>function initWatch (vm: Component, watch: Object) {
//...
createWatcher(vm, key, handler[i])
}/<code>

注意,没有贴出 initMethods 方法,因为如果它和 props 重名,会覆盖掉 props 的定义。所以它不具备数据响应功能。只是因为流程关系,放在了 props 之后。

下面开始 Vue 里的数据响应说明。

从 initData 开始,observe 观察对象

因为 data 是我们用的最多的属性,同时所以的数据都在此定义,initData 也是 vue 初始化状态中最代表性的方法,懂了它,其他的初始化方法也会很快理解。

先是做一系列和 props、methods 的校验 warn 判断,最后执行 observe 方法:

<code>function initData (vm: Component) {
//...
observe(data, true /* asRootData */)
}/<code>

observe 翻译为:观察。此处它的入参是 data,所以会观察 data 对象。也是整个数据响应的入口。

所以抱着这个思路来看后续的代码:

<code>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)
}
//...
return ob
}/<code>

此方法将获得 ob 对象,此对象将从 Observer 观察者中创建获取 or 从当前 value 中的 _ ob_ 属性中获取。

注意,只有数组,对象等复杂类型数据才会被创建观察者对象。要是像普通的 Number、String 将不会被观察。

观察者 Observer

下面是 Observer 类方法的代码:

<code>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 through all properties 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])
}
}

/**
* Observe a list of Array items.

*/
observeArray (items: Array) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
/<code>

所有的内容将围绕构造器 constructor 开始。


通过 def 方法,定义 _ob_ 属性

构造器中会通过 def 方法来定义 _ob_ 属性:

<code>constructor (value: any) {
def(value, '__ob__', this)
}/<code>

def 方法中,将见到 vue 所用"活"的 defineProperty 方法:

<code>export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}/<code>

通过

defineProperty 原生方法,往当前 value 对象(data)新添加 _ ob_ 属性,并且设置相关的对象属性描述特征。

解释下 def 的传入参数:obj 是我们 vm.$options 中设置的 data 对象,key _ ob_ 属性,val Observer this 引用(属性包括:value,dep,vmCount):

Vue 源码浅析:数据动态响应-观察者对象 Observer



通过 walk 方法,遍历 data 属性

之后,构造器最后通过 walk 方法,将 vm.$options.data 上所有的对象属性统统执行 defineReactive 方法:

<code>constructor (value: any) {
//...
this.walk(value)
}/<code>
<code>walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}/<code>

通过 defineReactive 对 data 属性赋予动态响应能力

因为涉及 Dep 模块,先大概了解此方法的简化结构:

<code>export function defineReactive (
obj: Object,
key: string,
val: any,

customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
//...
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
//...
},
set: function reactiveSetter (newVal) {
//...
}
})
}/<code>

传入 defineReactive 的 obj 对象会是这个样子(这也是 def 方法执行后的结果):

Vue 源码浅析:数据动态响应-观察者对象 Observer


接下来看 defineReactive 内部的逻辑。

创建 Dep 对象

在这里会创建一个新的 Dep 依赖订阅对象:

<code>const dep = new Dep()/<code>

初始化已定义的 getter/setter

获取 data 对象上属性的具体描述说明,并且取得 getter/setter 访问器属性:

<code>const property = Object.getOwnPropertyDescriptor(obj, key)
const getter = property && property.get
const setter = property && property.set/<code>

注意,会根据 arguments 参数预先执行 setter 方法:

<code>if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}/<code>

然后会重新对 data 对象上的属性来定义 getter/setter 访问器属性:

<code>let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
//...

},
set: function reactiveSetter (newVal) {
//...
}
})/<code>

reactiveGetter

先看 getter 的定义:

<code>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
}
//...
}/<code>

当模板解析后,就会调用其中设置的变量(如下,message),从而触发 get 方法:

<code>

{{ message }}
/<code>

这里会判断 Dep.target ,是否有监听对象 Watcher。

<code>if (Dep.target) {
//...
}/<code>

具体哪里对 Dep.target 进行了赋值,见下篇中的:依赖订阅器 Dep > 哪里设置 Dep target

我们先假设 Dep.target 存在,那么将执行 dep.depend 方法;如果子属性可以被观察,也将进行依赖解析:

<code>if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
//..
}
}/<code>

Dep 模块具体再看 depend 的作用。

reactiveSetter

setter 相对简单,主要流程如下:

  • 当对 data 中的属性更新时,先触发上面的 getter 方法。
  • 然后判断设置的 newVal 和之前的 value 有无不同,如果未发生改变则直接 return。
  • 如果有定义的 setter 方法,则会执行直接更新 data 对象;反之,对 val 赋予新值。
  • 最后对 newVal 进行 observe 观察,并通知 dep.notify 。
<code>set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
// #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()
}/<code>

总结

上面是执行 observe 观察方法后,执行的相关过程,通过 Observer 方法类创建观察者对象,每个观察者都具备 __ob__属性。

接着通过 defineReactive 对 options.data 上的属性进行数据动态响应的设置,其核心就是通过 getter/setter 建立起观察机制。


分享到:


相關文章: