事件中心-event

288 阅读2分钟

本节简单看一下 事件中心-event


new Vue() 的初始化过程中会执行事件的初始化 initEvents() 函数,在该函数内定义了 vm._events = {} 属性,它是当前实例上的自定义事件的集合,也可称为事件调度中心。还定义了标志位 vm._hasHookEvent = false; 表示是否监听组件的钩子函数,例:@hook:created=""this.$on('hook:created', fn).

然后又在同文件的 eventMixin() 函数内定义了 $on()、$once()、$off()、$emit() 四个函数,这也是接下来分析的重点内容。


$on(event, fn) 函数接收两个参数,事件名:event、fn:回调函数。首先对事件名 event 类型进行判断,如果 event 为数组则遍历并递归调用 $on() 函数,否则判断在 vm._events 属性上是否有对应的事件名,该事件名对应的值为数组类型。若存在直接 push(fn),若没有则创建为空数组,然后 push(fn) 进去。例如:vm._events[event] = []

$emit(event) 函数接收一个事件名 event 参数,也可以继续传递其它参数。拿到事件名以后首先从事件调度中心拿到对应的事件名的回调函数的集合 cbs,然后将其它参数转为数组,循环遍历回调函数集合 cbs 并执行 invokeWithErrorHandling() 函数,在该函数内判断如果有参数则执行 handler.apply(vm,args) 否则执行 handler.call(vm) 函数。

$once(event, fn) 函数接收两个参数,event:事件名、fn:回调函数。在该方法内注册的函数仅仅执行一次回调,然后移除该事件。首先将回调函数 fn 挂载到内部函数 on() 上,即为 on.fn = fn;,再通过 $on(event, on) 函数将 on() 添加到对应的事件中心。通过 $emit() 函数触发事件名时执行 on() 函数,在 on() 函数内执行 $off(event, on) 函数从对应的事件名上将 on() 函数移除,然后再执行传入的回调函数 fn(),这个时候事件中心已经没有了 on() 函数,回调也仅仅执行了一次。

$off(event, fn) 函数移除自定义事件监听函数。分类如下:

  1. 如果没有提供参数,则移除所有的事件监听器。
  2. 如果只提供了事件名 event,则移除该事件名对应的所有的回调函数。
  3. 如果同时提供了事件名与回调函数,则只移除事件名对应的回调函数 fn

$off(event, fn) 函数理解:

  • 首先判断函数参数长度,如果没有参数,则直接清空事件中心。
  • 如果 event 为数组则遍历递归执行 $off(event[i], fn) 函数。
  • event 为字符串类型,则获取到回调函数集合 cbs,如果回调函数 fn 不存在则直接清空 vm._events[event] = null;,如果回调函数 fn 存在则循环遍历集合 cbs,执行cbs.splice(i, 1) 从集合中删除回调函数 fn .

父子组件的通信也是经常遇到的,接下来分析一下:

父组件在经过编译模板后,会将定义在子组件上的自定义事件 test 及其回调函数 handler() 通过 $on() 函数添加到子组件的事件中心,当子组件通过 $emit 触发该自定义事件 test 时,会在子组件的事件中心找到该 test 并执行其回调函数 handler(),不过因为回调函数是在父组件作用域内定义的,所以看起来就像是父子组件之间通信般。