深入理解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 相关文章推荐
jquery select多选框的左右移动 具体实现代码
Jul 03 Javascript
JS限制Textarea文本域字符个数的具体实现
Aug 02 Javascript
Flexigrid在IE下不显示数据的有效处理方法
Sep 04 Javascript
浏览器兼容性问题大汇总
Dec 17 Javascript
bootstrap实现每隔5秒自动轮播效果
Dec 20 Javascript
详谈js中window.location.search的用法和作用
Feb 13 Javascript
node+vue实现用户注册和头像上传的实例代码
Jul 20 Javascript
判断div滑动到底部的scroll实例代码
Nov 15 Javascript
Vue引用Swiper4插件无法重写分页器样式的解决方法
Sep 27 Javascript
vue中利用iscroll.js解决pc端滚动问题
Feb 15 Javascript
微信小程序单选框自定义赋值
May 26 Javascript
Vue 数据响应式相关总结
Jan 28 Vue.js
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
使用CodeIgniter的类库做图片上传
2014/06/12 PHP
Mac系统下安装PHP Xdebug
2018/03/30 PHP
Laravel如何创建服务器提供者实例代码
2019/04/15 PHP
js动态加载以及确定加载完成的代码
2011/07/31 Javascript
Javascript对象中关于setTimeout和setInterval的this介绍
2012/07/21 Javascript
js replace 与replaceall实例用法详解
2013/08/03 Javascript
jQuery实现倒计时按钮功能代码分享
2014/09/03 Javascript
JavaScript判断用户是否对表单进行了修改的方法
2015/03/18 Javascript
jquery获取复选框的值的简单实例
2016/05/26 Javascript
Bootstrap编写一个兼容主流浏览器的受众门户式风格页面
2016/07/01 Javascript
JavaScript之cookie技术详解
2016/11/18 Javascript
实现JavaScript高性能的数据存储
2016/12/11 Javascript
前端js弹出框组件使用方法
2020/08/24 Javascript
Vue2几种常见开局方式详解
2017/09/09 Javascript
ES6使用Set数据结构实现数组的交集、并集、差集功能示例
2017/10/31 Javascript
JS中判断某个字符串是否包含另一个字符串的五种方法
2018/05/03 Javascript
JS实现鼠标按下拖拽效果
2020/07/23 Javascript
[49:27]LGD vs OG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
用Python实现一个简单的能够发送带附件的邮件程序的教程
2015/04/08 Python
在Django中限制已登录用户的访问的方法
2015/07/23 Python
Python使用win32com模块实现数据库表结构自动生成word表格的方法
2018/07/17 Python
python 将list转成字符串,中间用符号分隔的方法
2018/10/23 Python
神经网络相关之基础概念的讲解
2018/12/29 Python
Python3中_(下划线)和__(双下划线)的用途和区别
2019/04/26 Python
pytorch torch.expand和torch.repeat的区别详解
2019/11/05 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
2020/01/09 Python
TensorFlow Saver:保存和读取模型参数.ckpt实例
2020/02/10 Python
python DES加密与解密及hex输出和bs64格式输出的实现代码
2020/04/13 Python
Python RabbitMQ实现简单的进程间通信示例
2020/07/02 Python
python语言实现贪吃蛇游戏
2020/11/13 Python
初中中等生评语
2014/12/29 职场文书
2015年食堂工作总结报告
2015/04/23 职场文书
初中同学会致辞
2015/08/01 职场文书
如何做好员工培训计划?
2019/07/09 职场文书
Python编程super应用场景及示例解析
2021/10/05 Python
pandas进行数据输入和输出的方法详解
2022/03/23 Python