day128-vuejs源码-事件机制

前言

vuejs源码 v2.x.x 的实例方法 / 事件,$on,$once,$off,$emit

代码

// <.../core/instance/events.js>  on 20 Dec 2018
// $on
// 监听当前实例上的自定义事件。事件可以由vm.$emit触发。回调函数会接收所有传入事件触发函数的额外参数。-- https://cn.vuejs.org/v2/api/#vm-on
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
// $on可以是数组值
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)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup(注:利用布尔值标识是否注册时存在钩子事件,而不是利用hash查询)
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}

// $once
// 监听一个自定义事件,但是只触发一次,在第一次触发之后移除监听器。-- https://cn.vuejs.org/v2/api/#vm-once
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
// $off表示将事件销毁,第一执行之后就销毁
vm.$off(event, on)
// 调用方法
fn.apply(vm, arguments)
}
on.fn = fn
// 监听事件
vm.$on(event, on)
return vm
}
// $off
// 移除自定义事件监听器。
// 如果没有提供参数,则移除所有的事件监听器;
// 如果只提供了事件,则移除该事件所有的监听器;
// 如果同时提供了事件与回调,则只移除这个回调的监听器。https://cn.vuejs.org/v2/api/#vm-off
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
if (!arguments.length) {
// 返回创建的空对象
vm._events = Object.create(null)
return vm
}
// array of events
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
// 递归调用?
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
// 事件不存在则返回本身
if (!cbs) {
return vm
}
// 事件置空
if (!fn) {
vm._events[event] = null
return vm
}
// specific handler
// 找对应的方法fn,找到就移除
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return 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]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
// toArray:类数组转为数组
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 来自:<src/core/util/error.js>,避免嵌套调用时多次触发
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
文章作者: lmislm
文章链接: http://lmislm.com/2019/05/20/2019-05-20/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LMISLMのBlog