instanceEventEmitter.ts 2.5 KB

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