在Vue初始化(_init)时会调用initEvents函数来对事件进行初始化。initEvents中在Vue实例上创建了一个空对象用来存放事件
vm._events = Object.create(null)
vue的事件系统定义在/src/core/instance/events.js中的eventsMixin函数里。
vm.$on
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn)
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
第一个参数代表了事件名,如果传入的是一个数组,那就新循环这个数组递归调用$on。将注册的事件和回调以键值对的形式存储到 vm._events 对象中:vm._events = { eventA: [fn1, ...] }。
vm.$emit
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
//报错
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
从 vm._events 对象上根据事件名拿到当前事件的回调函数数组。获得参数后,循环回调函数数组,通过invokeWithErrorHandling包裹后调用每一个函数并捕获异常。
vm.$off
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
const cbs = vm._events[event]
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event] = null
return vm
}
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
如果调用$off时不传递任何参数,则会清空整个事件对象(vm._events)。
如果第一个参数是一个数组,怎会循环这个数组,递归调用$off。
如果参数只传了一个事件名,则将事件对象中这个事件的函数数组清空。
如果参数传递了一个事件名,并切第二个参数是一个具体函数,那么循环这个事件名对应的函数数组,找到对应的函数并移除。
vm.$once
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
$once只调用一次,实际还是调用了$on方法,在执行回调函数时会先移除本身,然后再执行第二个参数对应的函数