一、响应式对象
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) {
...
}
})
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!