写在前面

因为对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$once$off$emit

初始化事件

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

  1. {
  2. eventName: [func1, func2, func3]
  3. }

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

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

$on

emit触发。

  1. Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
  2. const vm: Component = this
  3. /*如果是数组的时候,则递归$on,为每一个成员都绑定上方法*/
  4. if (Array.isArray(event)) {
  5. for (let i = 0, l = event.length; i < l; i++) {
  6. this.$on(event[i], fn)
  7. }
  8. } else {
  9. (vm._events[event] || (vm._events[event] = [])).push(fn)
  10. // optimize hook:event cost by using a boolean flag marked at registration
  11. // instead of a hash lookup
  12. /*这里在注册事件的时候标记bool值也就是个标志位来表明存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
  13. if (hookRE.test(event)) {
  14. vm._hasHookEvent = true
  15. }
  16. }
  17. return vm
  18. }

$once

$once监听一个只能触发一次的事件,在触发以后会自动移除该事件。

  1. Vue.prototype.$once = function (event: string, fn: Function): Component {
  2. const vm: Component = this
  3. function on () {
  4. /*在第一次执行的时候将该事件销毁*/
  5. vm.$off(event, on)
  6. /*执行注册的方法*/
  7. fn.apply(vm, arguments)
  8. }
  9. on.fn = fn
  10. vm.$on(event, on)
  11. return vm
  12. }

$off

$off用来移除自定义事件

  1. Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
  2. const vm: Component = this
  3. // all
  4. /*如果不传参数则注销所有事件*/
  5. if (!arguments.length) {
  6. vm._events = Object.create(null)
  7. return vm
  8. }
  9. // array of events
  10. /*如果event是数组则递归注销事件*/
  11. if (Array.isArray(event)) {
  12. for (let i = 0, l = event.length; i < l; i++) {
  13. this.$off(event[i], fn)
  14. }
  15. return vm
  16. }
  17. // specific event
  18. const cbs = vm._events[event]
  19. /*Github:https://github.com/answershuto*/
  20. /*本身不存在该事件则直接返回*/
  21. if (!cbs) {
  22. return vm
  23. }
  24. /*如果只传了event参数则注销该event方法下的所有方法*/
  25. if (arguments.length === 1) {
  26. vm._events[event] = null
  27. return vm
  28. }
  29. // specific handler
  30. /*遍历寻找对应方法并删除*/
  31. let cb
  32. let i = cbs.length
  33. while (i--) {
  34. cb = cbs[i]
  35. if (cb === fn || cb.fn === fn) {
  36. cbs.splice(i, 1)
  37. break
  38. }
  39. }
  40. return vm
  41. }

$emit

$emit用来触发指定的自定义事件。

  1. Vue.prototype.$emit = function (event: string): Component {
  2. const vm: Component = this
  3. if (process.env.NODE_ENV !== 'production') {
  4. const lowerCaseEvent = event.toLowerCase()
  5. if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
  6. tip(
  7. `Event "${lowerCaseEvent}" is emitted in component ` +
  8. `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
  9. `Note that HTML attributes are case-insensitive and you cannot use ` +
  10. `v-on to listen to camelCase events when using in-DOM templates. ` +
  11. `You should probably use "${hyphenate(event)}" instead of "${event}".`
  12. )
  13. }
  14. }
  15. let cbs = vm._events[event]
  16. if (cbs) {
  17. /*将类数组的对象转换成数组*/
  18. cbs = cbs.length > 1 ? toArray(cbs) : cbs
  19. const args = toArray(arguments, 1)
  20. /*遍历执行*/
  21. for (let i = 0, l = cbs.length; i < l; i++) {
  22. cbs[i].apply(vm, args)
  23. }
  24. }
  25. return vm
  26. }

关于

作者:染陌

Email:answershuto@gmail.com or answershuto@126.com

Github: https://github.com/answershuto

Blog:http://answershuto.github.io/

知乎主页:https://www.zhihu.com/people/cao-yang-49/activities

知乎专栏:https://zhuanlan.zhihu.com/ranmo

掘金: https://juejin.im/user/58f87ae844d9040069ca7507

osChina:https://my.oschina.net/u/3161824/blog

转载请注明出处,谢谢。

欢迎关注我的公众号

Vue.js源码——事件机制的更多相关文章

  1. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...

  2. vue.js源码精析

    MVVM大比拼之vue.js源码精析 VUE 源码分析 简介 Vue 是 MVVM 框架中的新贵,如果我没记错的话作者应该毕业不久,现在在google.vue 如作者自己所说,在api设计上受到了很多 ...

  3. 2018-11-23 手工翻译Vue.js源码:尝试重命名标识符与文本

    续前文: 手工翻译Vue.js源码第一步:14个文件重命名 对core/instance/索引中的变量, 方法进行重命名如下(题图): import { 混入初始化 } from './初始化' im ...

  4. Vue.js 源码分析(一) 代码结构

    关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...

  5. 从Vue.js源码角度再看数据绑定

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  6. vue源码分析—Vue.js 源码构建

    Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下.(Rollup 中文网和英文网) 构建脚本 通常一个基于 NPM 托管的项目都会有一个 package.j ...

  7. 【转】从Vue.js源码看异步更新DOM策略及nextTick

    在使用vue.js的时候,有时候因为一些特定的业务场景,不得不去操作DOM,比如这样: <template> <div> <div ref="test" ...

  8. vue.js源码学习分享(一)

    今天看了vue.js源码  发现非常不错,想一边看一遍写博客和大家分享 /** * Convert a value to a string that is actually rendered. *转换 ...

  9. 嗨,让我带你逐行剖析Vue.js源码

    本项目受到了阮一峰老师的肯定,已刊登在阮一峰老师微信公众号的科技爱好者周刊第87期,同时也被多个微博大V转发,短短一个月时间内在github上star数量就已经突破2k! Hello,大家好,我最近在 ...

随机推荐

  1. 分享如何使用PHP将URL地址参数进行加密传输提高网站安全性

    大家在使用PHP进行GET或POST提交数据时,经常会在URL带着参数进行传递,比如www.mdaima.com/get.php?id=1&page=5,这里就将id编号和page页码进行了参 ...

  2. php 邓士鹏

    // $is_company = $_groupid > 5 || ($_groupid == 4 && $user['regid'] > 5); $_E = ($MOD[ ...

  3. dedecms_分页技术

    <ul>{dede:list pagesize='30'} <li><a href="[field:arcurl/]">[field:title ...

  4. Angular 4 自定义组件封装遇见的一些事儿

    你用Angular 吗? 一.介绍 说说封装Angular 组建过程中遇见的一些问题和感悟.用久了Angular,就会遇见很多坑,许多基于Angular开发的框架最喜欢做的事情就是封装组件,然后复用. ...

  5. Java数据持久层框架 MyBatis之背景知识一

    对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...

  6. 底部粘连(stiky footer)布局

    前面的话 在网页设计中,Sticky footers设计是最古老和最常见的效果之一,大多数人都曾经经历过.它可以概括如下:如果页面内容不够长的时候,页脚块粘贴在视窗底部:如果内容足够长时,页脚块会被内 ...

  7. bootstrap-table 表格加载中....处理

    $('#table').bootstrapTable({data:[]}); $('#table').bootstrapTable("showLoading"); ajax数据加载 ...

  8. junit--eclipse插件

    现在比较火的IDE是JIDE,但是我一直在使用eclipse.对eclipse比较熟悉了,也有了感情了.这里就以eclipse为例,来整理下eclipse中junit插件的使用. 添加junit包到自 ...

  9. 流API--流的映射

    很多时候,将一个流的元素映射到另外一个流很有帮助.映射操作最具代表的就是map()方法.实际编码中,我们会经常用到,所以这里专门整理一篇博客. 考虑如下情景,对于一个包含了姓名,电话,年龄等属性构成的 ...

  10. Java虚拟机栈和本地方法栈

    Java虚拟机栈的特征 线程私有 后进先出(LIFO)栈 存储栈帧,支持Java方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 Java ...