这里初始化了props,methods,data,computed以及watch。
function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
这里的_watchers用于存放watch:{}中的watcher,以及用户通过$watch创建的watcher。然后获取$options上的props,methods,data,computed,watch来进行初始化。
initProps
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
const keys = 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)
defineReactive(props, key, value)
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
这里的propsData是父组件或用户提供的props数据。而$options.prop是用户在子组件内定义的props:{}。循环$options.props,将props的每一个key缓存在vm.$options._propKeys上来优化性能:将来更新props时只需要遍历vm.$options._propKeys数组即可得到所有props的key。如果当前实例不是根组件,即isRoot是false,则不用对props的值进行响应式处理。这里通过toggleObserving方法将变量shouldObserve置为false。在observe方法中对于shouldObserve为false时不会给值添加响应式标记__ob__。
validateProp
function validateProp (
key: string,
propOptions: Object,
propsData: Object,
vm?: Component
): any {
const prop = propOptions[key]
const absent = !hasOwn(propsData, key)
let value = propsData[key]
const booleanIndex = getTypeIndex(Boolean, prop.type)
if (booleanIndex > -1) {
if (absent && !hasOwn(prop, 'default')) {
value = false
} else if (value === '' || value === hyphenate(key)) {
const stringIndex = getTypeIndex(String, prop.type)
if (stringIndex < 0 || booleanIndex < stringIndex) {
value = true
}
}
}
if (value === undefined) {
value = getPropDefaultValue(vm, prop, key)
const prevShouldObserve = shouldObserve
toggleObserving(true)
observe(value)
toggleObserving(prevShouldObserve)
}
if (
process.env.NODE_ENV !== 'production' &&
!(__WEEX__ && isObject(value) && ('@binding' in value))
) {
assertProp(prop, key, value, vm, absent)
}
return value
}
通过validateProp获取prop的值:这里对prop是Boolean类型的进行了特殊处理。当在父组件中没有给子组件传递对应的prop数据,并且子组件没有给prop设置default默认值时那么value就是false。如果在父组件上传了值为空字符串,或者值与key相等,比如这几中情况:
<child userName=""></child>
<child userName></child>
<child userName="user-name"></child>
<child name="name"></child>
user-name会被hyphenate转为驼峰。这几种情况value都是true。
如果在子组件中的props中定义了,但父组件中没有传递进来。那么通过getPropDefaultValue去获取默认值:没定义default时value就是undefined。如果有default则value就是default。对于array和object类型default会传入一个函数。那就调用函数获取default。然后通observe(value)响应式侦测这个默认值。
最后通过defineReactive将props添加到vm._props上。然后通过proxy将_props代理到实例上。通过this.xxx就能获取props。
initMethods
function initMethods (vm: Component, methods: Object) {
const props = vm.$options.props
for (const key in methods) {
if (process.env.NODE_ENV !== 'production') {
if (typeof methods[key] !== 'function') {
//error
}
if (props && hasOwn(props, key)) {
//error
}
if ((key in vm) && isReserved(key)) {
//error
}
}
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
这里会校验用户提供的methods中每一项是否是函数,名称是否与props上的重名。校验methos中的key与Vue实例上已有的方法是否重叠,一般是一些内置方法,比如以 $ 和_开头的方法。最后将方法绑定到实例上。可以通过this.xxx()调用。
initData
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// 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 (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)) {
proxy(vm, `_data`, key)
}
}
observe(data, true /* asRootData */)
}
首先判断用户提供的data是函数还是对象,如果是函数则通过getData去获取data并赋值给vm._data。然后校验data中的key是否与methods和props重名。然后将_data代理到实例上。最后通过observe去观测,使data响应式。
initComputed
const computedWatcherOptions = { lazy: true }
function initComputed (vm: Component, computed: Object) {
const watchers = vm._computedWatchers = Object.create(null)
const isSSR = isServerRendering()
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${key}".`,
vm
)
}
if (!isSSR) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm)
}
}
}
}
如果计算属性的值是一个函数就将函数赋值给getter,如果是一个对象,就获取它的get函数。然后创建一个watcher,watcher的option中有lazy属性,同时将这个watcher存放在vm._computedWatchers中。如果不与data,props,或实例中的其他属性重名,就调用defineComputed。
defineComputed
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
`Computed property "${key}" was assigned to but it has no setter.`,
this
)
}
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
首先构造属性描述符(get、set),对计算属性值时函数还是对象进行特殊处理,对于get进行处理成createComputedGetter。
createComputedGetter
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
首先获取之前保存的watcher。因为之前创建watcher时的option中lazy为true,所以Watcher内部的dirty属性也为true,因此首次会调用watcher.evaluate(),evaluate函数中会调用计算属性的get去获取值,并将dirty置为false。并将值保存在watcher的value上。计算属性就是获取的这个value。因此第二次执行时因为dirty是false所以不会再调用get方法。待页面更新后,wathcer.update方法会将watcher.dirty重新置为 true。
随后判断Dep.target是否存在,如果存在,则调用watcher.depend方法。这段代码的目的在于将读取计算属性的那个Watcher添加到计算属性所依赖的所有状态的依赖列表中。就是让读取计算属性的那个Watcher持续观察计算属性所依赖的状态的变化。
initWatch
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.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 | Function,
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)
}
根据用户提供的watch通过createWatcher去创建watcher,如果是函数数组,则循环调用createWatcher,最终去调用$watch方法。
proxy
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)
}
这里通过Object.defineProperty来将key代理到target上。