- 从上一章 new Vue 发生了什么,我们可以清除了解到 new Vue 做的一系列初始化。这一章我们具体看看,其中的 initState 初始化具体做了什么。
/src/core/instance/state.js
export function initState(vm: Component) {
vm._watchers = []
const opts = vm.$options
/*初始化props*/
if (opts.props) initProps(vm, opts.props)
/*初始化methods*/
if (opts.methods) initMethods(vm, opts.methods)
/*初始化data*/
if (opts.data) {
initData(vm)
} else {
observe((vm._data = {}), true /* asRootData */)
}
/*初始化computed*/
if (opts.computed) initComputed(vm, opts.computed)
/*初始化watch*/
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
从该函数不难看出:
initProps
初始化 propsinitMethods
初始化 methodsinitData
初始化 datainitComputed
初始化 computedinitWatch
初始化 watch
那么我们接着具体分析initData
具体做了什么?
/src/core/instance/state.js
function initData(vm: Component) {
let data = vm.$options.data
// 判断是否合法data,是否function
// vm._data,我们也可以通过这个方式访问我们.vue文件的data属性。但是不建议这样去获取。
data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
// 判断如果不是对象
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' &&
warn(
'data functions should return an object:\n' +
'https://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
// 循环对比data与props、methods,确保它们不能重名
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(`Method "${key}" has already been defined as a data property.`, vm)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' &&
warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
// 判断key不是 _或者$ 开头的属性,则把该key挂到vm._data上。
proxy(vm, `_data`, key)
}
}
// 处理完上述后,通过 observe 方法对数据进行响应式处理
// observe data
observe(data, true /* asRootData */)
}
上述代码主要执行了:
- 判断是否合法 data,并将其赋给
data
,vm._data
- 循化对比
data
和props
、methods
的属性,确保它们不与data
重名。 - 判断 key 不是 _或者$ 开头的属性,则把其代理到
vm._data
上。
/src/core/instance/state.js
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)
}
从这个proxy
方法我们不难看出,我们平时通过this.message
访问 data 的属性时。实际上是执行了vm._data.message
。然后通过proxy
函数,(vm 即 target,_data 即 sourceKey,message 即 key)。this[sourceKey][Key]
即执行this._data.message
,从而取到我们在 data 中设置的 message 参数的值。
全文从函数initState()
开始,依次处理:
initProps
初始化 propsinitMethods
初始化 methodsinitData
初始化 datainitComputed
初始化 computedinitWatch
初始化 watch
然后单独介绍initData()
做了什么
- 判断是否合法 data,并将其赋给
data
,vm._data
- 循化对比
data
和props
、methods
的属性,确保它们不与data
重名。 - 判断 key 不是 _或者$ 开头的属性,则把其代理到
vm._data
上。
最后,我们分析了proxy
函数。平时的this.message
即this._data.message
下一章节,先去分析其中比较简单的一部分initState(vm)
,这部分主要是 props,methods,data,computed,watch 初始化。
下一章: 【源码剖析】$mount 挂载
本章: 【源码剖析】initState 初始化
上一章:【源码剖析】new Vue 发生了什么