以vue2.7为学习版本
initMixin
在src/core/instance/init.ts
中,有一个initMixin
方法,这是vue初始化的开始
export function initMixin(Vue: typeof Component) {
Vue.prototype._init = function (options?: Record<string, any>) {
const vm: Component = this
// ...
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
}
在这个方法里面,在调用beforeCreate和created生命周期函数之间,初始化了inject、provide、initState,下面看一下initeState
做了一些什么
initState
src/core/instance/state.ts
export function initState(vm: Component) {
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
const ob = observe((vm._data = {}))
ob && ob.vmCount++
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
可以看出,初始化顺序是
1.props -> 2.methods -> 3.data -> 4. computed -> 5.watch
,所以平时写代码的时候,可以在data中通过this拿到props中属性、methods中的方法,
所以我们也可以在created生命周期中拿到props、methods、data、computed、watch,在beforeCreate中不可以,下面主要分析一下initData
初始化data
src/core/instance/state.ts
function initData(vm: Component) {
let data: any = vm.$options.data
data = vm._data = isFunction(data) ? getData(data, vm) : data || {}
if (!isPlainObject(data)) {
data = {}
__DEV__ &&
warn(
'data functions should return an object:\n' +
'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (__DEV__) {
if (methods && hasOwn(methods, key)) {
warn(`Method "${key}" has already been defined as a data property.`, vm)
}
}
if (props && hasOwn(props, key)) {
__DEV__ &&
warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
const ob = observe(data)
ob && ob.vmCount++
}
export function getData(data: Function, vm: Component): any {
// #7573 disable dep collection when invoking data getters
pushTarget()
try {
return data.call(vm, vm)
} catch (e: any) {
handleError(e, vm, `data()`)
return {}
} finally {
popTarget()
}
}
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
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)
}
首先拿到data判断是不是函数,如果是函数,调用getData,执行并返回 data.call(vm,vm),绑定data这个函数的this为当前组件实例,才能在data中通过this拿到props或者methods;
通过while循环,判断data中定义的属性名是否和props和methods选项中的属性名重复
调用observe(data)将data变成响应式
proxy(vm, _data
, key)通过Object.defineProperty
把data中的属性代理到vm实例上,因为vm._data = isFunction(data) ? getData(data, vm) : data || {}
,data选项中的属性赋值给了_data,我们能通过this.xxx的方式拿到this._data.xxx上的属性
observe 判断是否要监测
src/core/observer/index.ts
/**
* 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,
shallow?: boolean,
ssrMockReactivity?: boolean
): Observer | void {
if (!isObject(value) || isRef(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
(ssrMockReactivity || !isServerRendering()) &&
(isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value.__v_skip /* ReactiveFlags.SKIP */
) {
ob = new Observer(value, shallow, ssrMockReactivity)
}
return ob
}
首先参数value必须是对象,不能是ref,也不能是Vnode实例,否则直接返回;
if判断中,如果value已经被监测了,就直接返回value.ob,被监测了的数据会有一个_ob_属性;
else if条件中Object.isExtensible(value)
,说明对象是不能被冻结的,不然vue不会追踪变化,也就是说,当你页面中有些对象只是展示作用,可以使用Object.freeze()
来阻止vue将它变成响应式对象,提升页面性能;
Observer 对象和数组的监测方式不一样
src/core/observer/index.ts
/**
* 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 {
dep: Dep
vmCount: number // number of vms that have this object as root $data
constructor(public value: any, public shallow = false, public mock = false) {
// this.value = value
this.dep = mock ? mockDep : new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (isArray(value)) {
// 数组情况后面再看
} else {
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
const keys = Object.keys(value)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock)
}
}
}
}
export function def(obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
首先,通过def函数把自身实例添加到监测对象value的_ob_属性上面,代表该对象已经被监测了,所以上面observer函数中这段代码,
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ }
说明已经被监测的数据不需要再次监测了;
然后就是遍历
对象value的属性,使用defineReactive将其变成响应式 ,_ob_属性将不会被遍历,因为不可枚举,也用不着遍历,所以用def方法,而不是直接给value添加
defineReactive 添加get和set
src/core/observer/index.ts
/**
* Define a reactive property on an Object.
*/
export function defineReactive(
obj: object,
key: string,
val?: any,
customSetter?: Function | null,
shallow?: boolean,
mock?: 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) &&
(val === NO_INIITIAL_VALUE || arguments.length === 2)
) {
val = obj[key]
}
let childOb = !shallow && observe(val, false, mock)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 收集依赖
},
set: function reactiveSetter(newVal) {
// 触发更新
}
})
return dep
}
首先,通过Object.getOwnPropertyDescriptor
拿到属性描述符,configurable为false那就直接返回;
拿到要观测的属性的get和set,当getter不存在或者setter存在,并且val初始值为{}或者参数为2,就执行val=obj[key]
,给val赋值;
childOb如果不是shallow浅响应,那么就递归observe(val, false, mock)
,将每个对象属性都变成响应式,如果val不是对象,if (!isObject(value) || isRef(value) || value instanceof VNode) { return }
那observe返回undefined;
然后接下来就是访问这个属性的时候就会在get里面收集依赖,修改属性的时候就会在set中触发依赖更新