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


Posted in Javascript onSeptember 27, 2017

写在前面

因为对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
 }

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

Javascript 相关文章推荐
检测是否已安装 .NET Framework 3.5的js脚本
Feb 14 Javascript
artdialog的图片/标题以及关闭按钮不显示的解决方法
Jun 27 Javascript
使用AngularJS对路由进行安全性处理的方法
Jun 18 Javascript
纯javascript实现自动发送邮件
Oct 21 Javascript
Javascript中的return作用及javascript return关键字用法详解
Nov 05 Javascript
AngularJS中的表单简单入门
Jul 28 Javascript
jQuery Ajax前后端使用JSON进行交互示例
Mar 17 Javascript
微信小程序 setData的使用方法详解
Apr 20 Javascript
vuex的使用及持久化state的方式详解
Jan 23 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
Aug 08 Javascript
jquery实现动态添加附件功能
Oct 23 jQuery
小程序分页实践之编写可复用分页组件
Jul 18 Javascript
js截取字符串功能的实现方法
Sep 27 #Javascript
详解node+express+ejs+bootstrap构建项目
Sep 27 #Javascript
Three.js基础学习之场景对象
Sep 27 #Javascript
vue父组件中获取子组件中的数据(实例讲解)
Sep 27 #Javascript
Web开发使用Angular实现用户密码强度判别的方法
Sep 27 #Javascript
基于复选框demo(分享)
Sep 27 #Javascript
EasyUI框架 使用Ajax提交注册信息的实现代码
Sep 27 #Javascript
You might like
第十节 抽象方法和抽象类 [10]
2006/10/09 PHP
qq登录,新浪微博登录接口申请过程中遇到的问题
2014/07/22 PHP
PHP加密解密类实例分析
2015/04/20 PHP
PHP实现的线索二叉树及二叉树遍历方法详解
2016/04/25 PHP
javascript的trim,ltrim,rtrim自定义函数
2008/09/21 Javascript
Discuz! 6.1_jQuery兼容问题
2008/09/23 Javascript
jquery如何判断某元素是否具备指定的样式
2013/11/05 Javascript
js判断字符是否是汉字的两种方法小结
2014/01/03 Javascript
NodeJS学习笔记之FS文件模块
2015/01/13 NodeJs
jQuery结合CSS制作漂亮的select下拉菜单
2015/05/03 Javascript
jQuery EasyUI 菜单与按钮之创建简单的菜单和链接按钮
2015/11/18 Javascript
vue实现ToDoList简单实例
2017/02/07 Javascript
Node.js实现连接mysql数据库功能示例
2017/09/15 Javascript
如何将你的AngularJS1.x应用迁移至React的方法
2018/02/01 Javascript
微信小程序中的店铺评分组件及vue中用svg实现的评分显示组件
2018/11/16 Javascript
Vue 样式绑定的实现方法
2019/01/15 Javascript
微信小程序全局变量功能与用法详解
2019/01/22 Javascript
Element-UI中关于table表格的那些骚操作(小结)
2019/08/15 Javascript
在 Vue 中编写 SVG 图标组件的方法
2020/02/24 Javascript
[01:28:43]2014 DOTA2华西杯精英邀请赛5 24 DK VS CIS
2014/05/25 DOTA
[47:43]Alliance vs KG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
Python基于Floyd算法求解最短路径距离问题实例详解
2018/05/16 Python
python定向爬虫校园论坛帖子信息
2018/07/23 Python
PySide和PyQt加载ui文件的两种方法
2019/02/27 Python
Python3视频转字符动画的实例代码
2019/08/29 Python
基于tf.shape(tensor)和tensor.shape()的区别说明
2020/06/30 Python
css3闪亮进度条效果实现思路及代码
2013/04/17 HTML / CSS
英国男女奢华内衣和泳装购物网站:Figleaves
2017/01/28 全球购物
客服工作职责
2013/12/11 职场文书
出纳员岗位职责
2014/03/13 职场文书
职称评定自我鉴定
2014/03/18 职场文书
机械专业应届毕业生自荐书
2014/06/12 职场文书
老人再婚离婚协议书范本
2014/10/27 职场文书
订货会主持词
2015/07/01 职场文书
python如何在word中存储本地图片
2021/04/07 Python
JavaWeb 入门篇:创建Web项目,Idea配置tomcat
2021/07/16 Java/Android