深入理解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 相关文章推荐
js判断输入是否为数字的具体实例
Aug 03 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
Jun 05 Javascript
js实现滚动条滚动到页面底部继续加载
Dec 19 Javascript
AngularJS模块学习之Anchor Scroll
Jan 19 Javascript
jQuery 选择同时包含两个class的元素的实现方法
Jun 01 Javascript
js简单判断flash是否加载完成的方法
Jun 21 Javascript
javascript 实现动态侧边栏实例详解
Nov 11 Javascript
用node-webkit把web应用打包成桌面应用(windows环境)
Feb 01 Javascript
jQuery实现滚动到底部时自动加载更多的方法示例
Feb 18 jQuery
element-ui 设置菜单栏展开的方法
Aug 22 Javascript
Layui给switch添加响应事件的例子
Sep 03 Javascript
Vue使用预渲染代替SSR的方法
Jul 02 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
php实现简单的上传进度条
2015/11/17 PHP
thinkPHP数据库增删改查操作方法实例详解
2016/12/06 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
JavaScript Base64编码和解码,实现URL参数传递。
2006/09/18 Javascript
javascript操作数组详解
2014/12/17 Javascript
jQuery中animate动画第二次点击事件没反应
2015/05/07 Javascript
详解JavaScript基本类型和引用类型
2015/12/09 Javascript
JavaScript代码实现图片循环滚动效果
2020/03/19 Javascript
微信小程序 request接口的封装实例代码
2017/04/26 Javascript
JavaScript定义函数_动力节点Java学院整理
2017/06/27 Javascript
浅谈angular2 组件的生命周期钩子
2017/08/12 Javascript
JavaScript中正则表达式使数字、中文或指定字符高亮显示
2017/10/31 Javascript
Angular实现的自定义模糊查询、排序及三角箭头标注功能示例
2017/12/28 Javascript
网页爬虫之cookie自动获取及过期自动更新的实现方法
2018/03/06 Javascript
微信小程序websocket实现聊天功能
2020/03/30 Javascript
使用vue-router为每个路由配置各自的title
2018/07/30 Javascript
webpack4 处理SCSS的方法示例
2018/09/03 Javascript
vue+element导航栏高亮显示的解决方式
2019/11/12 Javascript
[07:55]2014DOTA2 TI正赛第三日 VG上演推进荣耀DKEG告别
2014/07/21 DOTA
Python中除法使用的注意事项
2014/08/21 Python
使用Eclipse如何开发python脚本
2018/04/11 Python
Python pandas.DataFrame调整列顺序及修改index名的方法
2019/06/21 Python
浅析python中while循环和for循环
2019/11/19 Python
AUC计算方法与Python实现代码
2020/02/28 Python
tensorflow 2.0模式下训练的模型转成 tf1.x 版本的pb模型实例
2020/06/22 Python
Elasticsearch py客户端库安装及使用方法解析
2020/09/14 Python
Foot Locker英国官网:美国知名运动产品零售商
2019/02/21 全球购物
美国厨房和园艺工具网上商店:Nestneed
2019/08/24 全球购物
意大利运动服减价商店:ScontoSport
2020/03/10 全球购物
4s店总经理岗位职责
2013/12/31 职场文书
销售督导岗位职责
2015/04/10 职场文书
如何写辞职信
2015/05/13 职场文书
迎新生欢迎词2015
2015/07/16 职场文书
2016消防宣传标语口号
2015/12/26 职场文书
死磕 java同步系列之synchronized解析
2021/06/28 Java/Android
根德5570型九灯四波段立体声收音机是电子管收音机的楷模 ? 再论5570
2022/04/05 无线电