vue版本:v2.7.10
src/core/instance/state.ts
export function initState(vm: Component) {
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
// Composition API
initSetup(vm)
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)
}
}
initProps
将vm.$options.propsData转化为响应式数据,挂到vm._props上,通过vm代理props,可以直接通过this访问props属性
function initProps(vm: Component, propsOptions: Object) {
// 将vm.$options.propsData的属性赋予给vm._props,并转化为响应式数据
const propsData = vm.$options.propsData || {}
const props = (vm._props = shallowReactive({}))
const keys: string[] = (vm.$options._propKeys = [])
const isRoot = !vm.$parent
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
if (__DEV__) {
const hyphenatedKey = hyphenate(key)
if (
isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)
) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
// 将通过vm代理vm._props,即可以直接通过vue实例的this访问props
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
initSetup
组合时API的实现,后续再研究
initMethods
将vm.$options.methods函数执行时的this设置为vm实例
function initMethods(vm: Component, methods: Object) {
...
for (const key in methods) {
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
initData
将vm.$options.data转化为响应式数据,挂到vm._data上,通过vm代理data,可以直接通过this访问data属性
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
)
}
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++
}
其中,observe用于将对象转换为响应式数据,在后续深入研究。
initComputed
function initComputed(vm: Component, computed: Object) {
const watchers = (vm._computedWatchers = Object.create(null))
for (const key in computed) {
const userDef = computed[key]
const getter = isFunction(userDef) ? userDef : userDef.get
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
}
将所有computed属性,新增Watcher至vm._computedWatchers中,Watcher在后续会有独立篇章深入研究。
initWatch
function initWatch(vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
function createWatcher(
vm: Component,
expOrFn: string | (() => any),
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}
Vue.prototype.$watch = function (
expOrFn: string | (() => any),
cb: any,
options?: Record<string, any>
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`
pushTarget()
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
return function unwatchFn() {
watcher.teardown()
}
}
}
遍历options中的watch,通过Vue.prototype.$watch为每个watch属性创建独立的Watcher