资讯专栏INFORMATION COLUMN

Vue.js源码——事件机制

Ethan815 / 1988人阅读

摘要:的内容如下存放事件名以及对应执行方法。如果是数组的时候,则递归,为每一个成员都绑定上方法这里在注册事件的时候标记值也就是个标志位来表明存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。

写在前面

因为对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的内容如下:

</>复制代码

  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

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

</>复制代码

  1. Vue.prototype.$on = function (event: string | Array, 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, 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

转载请注明出处,谢谢。

欢迎关注我的公众号

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88728.html

相关文章

  • vue源码解析-事件机制

    摘要:直接写了组件机制。今天看了下的关于事件的机制。源码都是基于最新的。绑定了事件回调函数的。初始化的时候,将中的方法代理到的同时修饰了事件的回调函数。对于事件有两个底层的处理逻辑。 上一章没什么经验。直接写了组件机制。感觉涉及到的东西非常的多,不是很方便讲。今天看了下vue的关于事件的机制。有一些些体会。写出来。大家一起纠正,分享。源码都是基于最新的Vue.js v2.3.0。下面我们来看...

    LuDongWei 评论0 收藏0
  • Vue.js 源码学习笔记

    摘要:实际上,我在看代码的过程中顺手提交了这个,作者眼明手快,当天就进行了修复,现在最新的代码里已经不是这个样子了而且状态机标识由字符串换成了数字常量,解析更准确的同时执行效率也会更高。 最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些。那么,就让我来吧:) 程序结构梳...

    darkbaby123 评论0 收藏0
  • Vue.js 源码学习笔记

    摘要:实际上,我在看代码的过程中顺手提交了这个,作者眼明手快,当天就进行了修复,现在最新的代码里已经不是这个样子了而且状态机标识由字符串换成了数字常量,解析更准确的同时执行效率也会更高。 最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些。那么,就让我来吧:) 程序结构梳...

    jsdt 评论0 收藏0
  • vue-router 实现分析

    摘要:而组件在创建时,又怎么会去调用呢这是由于将自身作为一个插件安装到了,通过注册了一个钩子函数,从而在之后所有的组件创建时都会调用该钩子函数,给了检查是否有参数,从而进行初始化的机会。 vue-router 是 Vue.js 官方的路由库,本着学习的目的,我对 vue-router 的源码进行了阅读和分析,分享出来给其他感兴趣的同学做个参考吧。 参考 源码:vuejs/vue-route...

    psychola 评论0 收藏0

发表评论

0条评论

Ethan815

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<