methods处理
在分析完props
相关逻辑后,我们接下来分析与methods
相关的逻辑,这部分相比于props
要简单得多。
export function initState (vm: Component) {
// 省略代码
const opts = vm.$options
if (opts.methods) initMethods(vm, opts.methods)
}
在initState()
方法中,调用了initMethods()
并传入了当前实例vm
和我们撰写的methods
。接下来,我们看一下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') {
warn(
`Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
if (props && hasOwn(props, key)) {
warn(
`Method "${key}" has already been defined as a prop.`,
vm
)
}
if ((key in vm) && isReserved(key)) {
warn(
`Method "${key}" conflicts with an existing Vue instance method. ` +
`Avoid defining component methods that start with _ or $.`
)
}
}
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
在以上代码中可以看到,initMethods()
方法实现中最重要的一段代码就是:
// 空函数
function noop () {}
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
它首先判断了我们定义的methods
是不是function
类型,如果不是则赋值为一个noop
空函数,如果是则把这个方法进行bind
绑定,其中传入的vm
为当前实例。这样做的目的是为了把methods
方法中的this
指向当前实例,使得我们能在methods
方法中通过this.xxx
的形式,很方便的访问到props
、data
以及computed
等与实例相关的属性或方法。
在开发环境下,它还做了如下几种判断:
- 必须为
function
类型。
// 抛出错误:Method sayHello has type null in the component definition.
// Did you reference the function correctly?
export default {
methods: {
sayHello: null
}
}
- 命名不能和
props
冲突。
// 抛出错误:Method name has already been defined as a prop.
export default {
props: ['name']
methods: {
name () {
console.log('name')
}
}
}
- 命名不能和已有的实例方法冲突。
// 抛出错误:Method $set conflicts with an existing Vue instance method.
// Avoid defining component methods that start with _ or $.
export default {
methods: {
$set () {
console.log('$set')
}
}
}
在分析完以上initMethods
流程后,我们能得到如下流程图: