一、响应式对象
vue是通过bject.defineProperty进行数据劫持,把数据变成响应式的,这也是vue的一个核心思想
在执行_init的时候会执行initState函数,initState中,会通过initProps和initData来初始化props和data
export function initState (vm: Component) {
// src/core/instance/state.js
...
if (opts.props) initProps(vm, opts.props)
if (opts.data) {
initData(vm)
}
...
}
initData函数会调用observe(data, true /* asRootData */)函数,observe函数首先会判断他是否是一个对象,如果不是一个对象,或者它是一个vnode,那么就不会接着执行。之后会判断传入的data是否有通过Observer类构造出的__ob__属性,如果有的话,则说明他已经是一个响应式的对象,则ob直接使用之前的data.__ob__,如果不满足,那么会判断shouldObserve,shouldObserve定义在当前文件,默认为true,通过toggleObserving方法可以进行值的修改,在initProps的时候,会判断props是否是一个根部的props,如果是(则不需要变为响应式),会通过toggleObserving先变为false,执行到最后再变为true。接着会判断他是否是一个数组,或对象,并且会通过Object.isExtensible判断他是否是一个可扩展的对象,所以如果想阻止data中的一些恒量变为响应式的,通过Object.freeze即可。最后会调用new Observer(value)
// src/core/observer/index.js
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
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
}
在执行new Observer的时候,会调用def(value, '__ob__', this),def定义在
src/core/util/lang.js下,他通过Object.defineProperty来控制这个对象属性的enumerable是true,还是false(是否可枚举)。当前的__ob__是不可被枚举的。然后会判断他是否是一个数组,如果不是一个数组,会调用walk方法,如果是数组,会遍历数组去调用observe方法,会再次new Observer这样会让数组中的对象最终都会调用walk函数。walk函数会遍历对象,然后调用defineReactive函数,这样就可以解释为什么会对__ob__做不可枚举的处理,因为__ob__使用者并不会手动去修改,也就不需要是一个响应式的属性。
// src/core/observer/index.js
/**
* Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
*/
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<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
defineReactive函数首先他会尝试拿到他原始的configurable,enumerable,value,writable,然后会去获取他的get和set,如果没有getter或者有setter(该属性是一个对象)并且该函数传入了两个参数,那么会去执行observe这样就保证了对象中的属性值是一个对象,也会变成响应式的。最后通过Object.defineProperty给该属性绑定getter(依赖收集)和setter(派发更新),而initProps最终也会调用defineReactive把props变为响应式的
/**
* Define a reactive property on an Object.
*/
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
}
// cater for pre-defined getter/setters
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 () {
...
},
set: function reactiveSetter (newVal) {
...
}
})
}