107d5ae555011673aa2b8727569130cd11a569623330c4c4f993b0f63590f96cfc1de398aae84631ea89bb31ea770549feda2d433183b537290eea17746548 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /* @flow */
  2. import {
  3. tip,
  4. toArray,
  5. hyphenate,
  6. formatComponentName,
  7. invokeWithErrorHandling
  8. } from '../util/index'
  9. import { updateListeners } from '../vdom/helpers/index'
  10. export function initEvents (vm: Component) {
  11. vm._events = Object.create(null)
  12. vm._hasHookEvent = false
  13. // init parent attached events
  14. const listeners = vm.$options._parentListeners
  15. if (listeners) {
  16. updateComponentListeners(vm, listeners)
  17. }
  18. }
  19. let target: any
  20. function add (event, fn) {
  21. target.$on(event, fn)
  22. }
  23. function remove (event, fn) {
  24. target.$off(event, fn)
  25. }
  26. function createOnceHandler (event, fn) {
  27. const _target = target
  28. return function onceHandler () {
  29. const res = fn.apply(null, arguments)
  30. if (res !== null) {
  31. _target.$off(event, onceHandler)
  32. }
  33. }
  34. }
  35. export function updateComponentListeners (
  36. vm: Component,
  37. listeners: Object,
  38. oldListeners: ?Object
  39. ) {
  40. target = vm
  41. updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
  42. target = undefined
  43. }
  44. export function eventsMixin (Vue: Class<Component>) {
  45. const hookRE = /^hook:/
  46. Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
  47. const vm: Component = this
  48. if (Array.isArray(event)) {
  49. for (let i = 0, l = event.length; i < l; i++) {
  50. vm.$on(event[i], fn)
  51. }
  52. } else {
  53. (vm._events[event] || (vm._events[event] = [])).push(fn)
  54. // optimize hook:event cost by using a boolean flag marked at registration
  55. // instead of a hash lookup
  56. if (hookRE.test(event)) {
  57. vm._hasHookEvent = true
  58. }
  59. }
  60. return vm
  61. }
  62. Vue.prototype.$once = function (event: string, fn: Function): Component {
  63. const vm: Component = this
  64. function on () {
  65. vm.$off(event, on)
  66. fn.apply(vm, arguments)
  67. }
  68. on.fn = fn
  69. vm.$on(event, on)
  70. return vm
  71. }
  72. Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
  73. const vm: Component = this
  74. // all
  75. if (!arguments.length) {
  76. vm._events = Object.create(null)
  77. return vm
  78. }
  79. // array of events
  80. if (Array.isArray(event)) {
  81. for (let i = 0, l = event.length; i < l; i++) {
  82. vm.$off(event[i], fn)
  83. }
  84. return vm
  85. }
  86. // specific event
  87. const cbs = vm._events[event]
  88. if (!cbs) {
  89. return vm
  90. }
  91. if (!fn) {
  92. vm._events[event] = null
  93. return vm
  94. }
  95. // specific handler
  96. let cb
  97. let i = cbs.length
  98. while (i--) {
  99. cb = cbs[i]
  100. if (cb === fn || cb.fn === fn) {
  101. cbs.splice(i, 1)
  102. break
  103. }
  104. }
  105. return vm
  106. }
  107. Vue.prototype.$emit = function (event: string): Component {
  108. const vm: Component = this
  109. if (process.env.NODE_ENV !== 'production') {
  110. const lowerCaseEvent = event.toLowerCase()
  111. if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
  112. tip(
  113. `Event "${lowerCaseEvent}" is emitted in component ` +
  114. `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
  115. `Note that HTML attributes are case-insensitive and you cannot use ` +
  116. `v-on to listen to camelCase events when using in-DOM templates. ` +
  117. `You should probably use "${hyphenate(event)}" instead of "${event}".`
  118. )
  119. }
  120. }
  121. let cbs = vm._events[event]
  122. if (cbs) {
  123. cbs = cbs.length > 1 ? toArray(cbs) : cbs
  124. const args = toArray(arguments, 1)
  125. const info = `event handler for "${event}"`
  126. for (let i = 0, l = cbs.length; i < l; i++) {
  127. invokeWithErrorHandling(cbs[i], vm, args, vm, info)
  128. }
  129. }
  130. return vm
  131. }
  132. }