instanceEventEmitter.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { isArray } from '@vue/shared'
  2. import type { ComponentInternalInstance } from '../component'
  3. import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
  4. import { DeprecationTypes, assertCompatEnabled } from './compatConfig'
  5. import type { ComponentPublicInstance } from '../componentPublicInstance'
  6. interface EventRegistry {
  7. [event: string]: Function[] | undefined
  8. }
  9. const eventRegistryMap = /*@__PURE__*/ new WeakMap<
  10. ComponentInternalInstance,
  11. EventRegistry
  12. >()
  13. export function getRegistry(
  14. instance: ComponentInternalInstance,
  15. ): EventRegistry {
  16. let events = eventRegistryMap.get(instance)
  17. if (!events) {
  18. eventRegistryMap.set(instance, (events = Object.create(null)))
  19. }
  20. return events!
  21. }
  22. export function on(
  23. instance: ComponentInternalInstance,
  24. event: string | string[],
  25. fn: Function,
  26. ): ComponentPublicInstance | null {
  27. if (isArray(event)) {
  28. event.forEach(e => on(instance, e, fn))
  29. } else {
  30. if (event.startsWith('hook:')) {
  31. assertCompatEnabled(
  32. DeprecationTypes.INSTANCE_EVENT_HOOKS,
  33. instance,
  34. event,
  35. )
  36. } else {
  37. assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
  38. }
  39. const events = getRegistry(instance)
  40. ;(events[event] || (events[event] = [])).push(fn)
  41. }
  42. return instance.proxy
  43. }
  44. export function once(
  45. instance: ComponentInternalInstance,
  46. event: string,
  47. fn: Function,
  48. ): ComponentPublicInstance | null {
  49. const wrapped = (...args: any[]) => {
  50. off(instance, event, wrapped)
  51. fn.apply(instance.proxy, args)
  52. }
  53. wrapped.fn = fn
  54. on(instance, event, wrapped)
  55. return instance.proxy
  56. }
  57. export function off(
  58. instance: ComponentInternalInstance,
  59. event?: string | string[],
  60. fn?: Function,
  61. ): ComponentPublicInstance | null {
  62. assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
  63. const vm = instance.proxy
  64. // all
  65. if (!event) {
  66. eventRegistryMap.set(instance, Object.create(null))
  67. return vm
  68. }
  69. // array of events
  70. if (isArray(event)) {
  71. event.forEach(e => off(instance, e, fn))
  72. return vm
  73. }
  74. // specific event
  75. const events = getRegistry(instance)
  76. const cbs = events[event!]
  77. if (!cbs) {
  78. return vm
  79. }
  80. if (!fn) {
  81. events[event!] = undefined
  82. return vm
  83. }
  84. events[event!] = cbs.filter(cb => !(cb === fn || (cb as any).fn === fn))
  85. return vm
  86. }
  87. export function emit(
  88. instance: ComponentInternalInstance,
  89. event: string,
  90. args: any[],
  91. ): ComponentPublicInstance | null {
  92. const cbs = getRegistry(instance)[event]
  93. if (cbs) {
  94. callWithAsyncErrorHandling(
  95. cbs.map(cb => cb.bind(instance.proxy)),
  96. instance,
  97. ErrorCodes.COMPONENT_EVENT_HANDLER,
  98. args,
  99. )
  100. }
  101. return instance.proxy
  102. }