深入理解Vue.js源码之事件机制

 更新时间:2017年09月27日 09:53:20   作者:曹阳  
本篇文章主要介绍了Vue.js源码之事件机制,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

写在前面

因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出。

文章的原地址:https://github.com/answershuto/learnVue

在学习过程中,为Vue加上了中文的注释https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以对其他想学习Vue源码的小伙伴有所帮助。
可能会有理解存在偏差的地方,欢迎提issue指出,共同学习,共同进步。

Vue事件API

众所周知,Vue.js为我们提供了四个事件API,分别是$on](https://cn.vuejs.org/v2/api/#vm-on-event-callback),[$once,$off](https://cn.vuejs.org/v2/api/#vm-off-event-callback),[$emit。

初始化事件

初始化事件在vm上创建一个_events对象,用来存放事件。_events的内容如下:

{
  eventName: [func1, func2, func3]
}

存放事件名以及对应执行方法。

/*初始化事件*/
export function initEvents (vm: Component) {
 /*在vm上创建一个_events对象,用来存放事件。*/
 vm._events = Object.create(null)
 /*这个bool标志位来表明是否存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
 vm._hasHookEvent = false
 // init parent attached events
 /*初始化父组件attach的事件*/
 const listeners = vm.$options._parentListeners
 if (listeners) {
  updateComponentListeners(vm, listeners)
 }
}

$on

$on方法用来在vm实例上监听一个自定义事件,该事件可用$emit触发。

 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++) {
    this.$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
   /*这里在注册事件的时候标记bool值也就是个标志位来表明存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
   if (hookRE.test(event)) {
    vm._hasHookEvent = true
   }
  }
  return vm
 }

$once

$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
 }

$off

$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
  /*如果event是数组则递归注销事件*/
  if (Array.isArray(event)) {
   for (let i = 0, l = event.length; i < l; i++) {
    this.$off(event[i], fn)
   }
   return vm
  }
  // specific event
  const cbs = vm._events[event]
  /*Github:https://github.com/answershuto*/
  /*本身不存在该事件则直接返回*/
  if (!cbs) {
   return vm
  }
  /*如果只传了event参数则注销该event方法下的所有方法*/
  if (arguments.length === 1) {
   vm._events[event] = null
   return vm
  }
  // specific handler
  /*遍历寻找对应方法并删除*/
  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

$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) {
   /*将类数组的对象转换成数组*/
   cbs = cbs.length > 1 ? toArray(cbs) : cbs
   const args = toArray(arguments, 1)
   /*遍历执行*/
   for (let i = 0, l = cbs.length; i < l; i++) {
    cbs[i].apply(vm, args)
   }
  }
  return vm
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • vue实现接口封装的实现示例

    vue实现接口封装的实现示例

    本文主要介绍了vue实现接口封装的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • Vuex中actions优雅处理接口请求的方法

    Vuex中actions优雅处理接口请求的方法

    在项目开发中,如果使用到了 vuex,通常我会将所有的接口请求单独用一个文件管理,这篇文章主要介绍了Vuex中actions如何优雅处理接口请求,业务逻辑写在 actions 中,本文给大家分享完整流程需要的朋友可以参考下
    2022-11-11
  • vue项目中echarts自适应问题的高级解决过程

    vue项目中echarts自适应问题的高级解决过程

    虽然老早就看过很多echarts的例子,但自己接触的项目中一直都没有真正用到过,直到最近才开始真正使用,下面这篇文章主要给大家介绍了关于vue项目中echarts自适应问题的高级解决过程,需要的朋友可以参考下
    2023-05-05
  • Vue.js第二天学习笔记(vue-router)

    Vue.js第二天学习笔记(vue-router)

    这篇文章主要为大家详细介绍了Vue.js第二天的学习笔记,关于vue-router的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • vue + Electron 制作桌面应用的示例代码

    vue + Electron 制作桌面应用的示例代码

    这篇文章主要介绍了vue + Electron 制作桌面应用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Vue computed计算属性总结记录

    Vue computed计算属性总结记录

    在vue中,有时候你需要对data中的数据进行处理,或者对抓取的数据进行处理之后再挂载呈现到标签中,这时候你就需要计算属性了,当然看到这里你可能还是不了解那下面我举几个实例并附代码解释
    2023-02-02
  • electron实现静默打印的示例代码

    electron实现静默打印的示例代码

    这篇文章主要介绍了electron实现静默打印的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • vue如何使用vant组件的field组件disabled修改默认样式

    vue如何使用vant组件的field组件disabled修改默认样式

    这篇文章主要介绍了vue如何使用vant组件的field组件disabled修改默认样式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 关于vue中标签的属性绑定值渲染问题

    关于vue中标签的属性绑定值渲染问题

    这篇文章主要介绍了关于vue中标签的属性绑定值渲染问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • VUE引入第三方js包及调用方法讲解

    VUE引入第三方js包及调用方法讲解

    今天小编就为大家分享一篇关于VUE引入第三方js包及调用方法讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03

最新评论