wip: instance event emitter api compat
This commit is contained in:
		
							parent
							
								
									9828ef0845
								
							
						
					
					
						commit
						db09805688
					
				| @ -18,6 +18,8 @@ export const enum DeprecationTypes { | ||||
|   INSTANCE_DELETE, | ||||
|   INSTANCE_MOUNT, | ||||
|   INSTANCE_DESTROY, | ||||
|   INSTANCE_EVENT_EMITTER, | ||||
|   INSTANCE_EVENT_HOOKS, | ||||
| 
 | ||||
|   OPTIONS_DATA_FN, | ||||
|   OPTIONS_DATA_MERGE, | ||||
| @ -132,6 +134,20 @@ const deprecations: Record<DeprecationTypes, DeprecationData> = { | ||||
|     link: `https://v3.vuejs.org/api/application-api.html#unmount` | ||||
|   }, | ||||
| 
 | ||||
|   [DeprecationTypes.INSTANCE_EVENT_EMITTER]: { | ||||
|     message: | ||||
|       `vm.$on/$once/$off() have been removed. ` + | ||||
|       `Use an external event emitter library instead.`, | ||||
|     link: `https://v3.vuejs.org/guide/migration/events-api.html` | ||||
|   }, | ||||
| 
 | ||||
|   [DeprecationTypes.INSTANCE_EVENT_HOOKS]: { | ||||
|     message: | ||||
|       `"hook:x" lifecycle events are no longer supported. ` + | ||||
|       `Use Composition API to dynamically register lifecycle hooks.`, | ||||
|     link: `https://v3.vuejs.org/api/composition-api.html#lifecycle-hooks` | ||||
|   }, | ||||
| 
 | ||||
|   [DeprecationTypes.OPTIONS_DATA_FN]: { | ||||
|     message: | ||||
|       `The "data" option can no longer be a plain object. ` + | ||||
| @ -169,10 +185,17 @@ const deprecations: Record<DeprecationTypes, DeprecationData> = { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const hasWarned: Record<string, boolean> = {} | ||||
| 
 | ||||
| export function warnDeprecation(key: DeprecationTypes, ...args: any[]) { | ||||
|   if (!__COMPAT__ || !__DEV__) { | ||||
|     return | ||||
|   } | ||||
|   const dupKey = key + args.join('') | ||||
|   if (hasWarned[dupKey]) { | ||||
|     return | ||||
|   } | ||||
|   hasWarned[dupKey] = true | ||||
|   const { message, link } = deprecations[key] | ||||
|   warn( | ||||
|     `[DEPRECATION] ${ | ||||
|  | ||||
							
								
								
									
										106
									
								
								packages/runtime-core/src/compat/eventEmitter.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								packages/runtime-core/src/compat/eventEmitter.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| import { isArray } from '@vue/shared' | ||||
| import { ComponentInternalInstance } from '../component' | ||||
| import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling' | ||||
| import { DeprecationTypes, warnDeprecation } from './deprecations' | ||||
| 
 | ||||
| interface EventRegistry { | ||||
|   [event: string]: Function[] | undefined | ||||
| } | ||||
| 
 | ||||
| const eventRegistryMap = /*#__PURE__*/ new WeakMap< | ||||
|   ComponentInternalInstance, | ||||
|   EventRegistry | ||||
| >() | ||||
| 
 | ||||
| export function getRegistry( | ||||
|   instance: ComponentInternalInstance | ||||
| ): EventRegistry { | ||||
|   let events = eventRegistryMap.get(instance) | ||||
|   if (!events) { | ||||
|     eventRegistryMap.set(instance, (events = Object.create(null))) | ||||
|   } | ||||
|   return events! | ||||
| } | ||||
| 
 | ||||
| export function on( | ||||
|   instance: ComponentInternalInstance, | ||||
|   event: string | string[], | ||||
|   fn: Function | ||||
| ) { | ||||
|   if (isArray(event)) { | ||||
|     event.forEach(e => on(instance, e, fn)) | ||||
|   } else { | ||||
|     const events = getRegistry(instance) | ||||
|     ;(events[event] || (events[event] = [])).push(fn) | ||||
|     if (__DEV__) { | ||||
|       if (event.startsWith('hook:')) { | ||||
|         warnDeprecation(DeprecationTypes.INSTANCE_EVENT_HOOKS) | ||||
|       } else { | ||||
|         warnDeprecation(DeprecationTypes.INSTANCE_EVENT_EMITTER) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return instance.proxy | ||||
| } | ||||
| 
 | ||||
| export function once( | ||||
|   instance: ComponentInternalInstance, | ||||
|   event: string, | ||||
|   fn: Function | ||||
| ) { | ||||
|   const wrapped = (...args: any[]) => { | ||||
|     off(instance, event, wrapped) | ||||
|     fn.call(instance.proxy, ...args) | ||||
|   } | ||||
|   wrapped.fn = fn | ||||
|   on(instance, event, wrapped) | ||||
|   return instance.proxy | ||||
| } | ||||
| 
 | ||||
| export function off( | ||||
|   instance: ComponentInternalInstance, | ||||
|   event?: string, | ||||
|   fn?: Function | ||||
| ) { | ||||
|   __DEV__ && warnDeprecation(DeprecationTypes.INSTANCE_EVENT_EMITTER) | ||||
|   const vm = instance.proxy | ||||
|   // all
 | ||||
|   if (!arguments.length) { | ||||
|     eventRegistryMap.set(instance, Object.create(null)) | ||||
|     return vm | ||||
|   } | ||||
|   // array of events
 | ||||
|   if (isArray(event)) { | ||||
|     event.forEach(e => off(instance, e, fn)) | ||||
|     return vm | ||||
|   } | ||||
|   // specific event
 | ||||
|   const events = getRegistry(instance) | ||||
|   const cbs = events[event!] | ||||
|   if (!cbs) { | ||||
|     return vm | ||||
|   } | ||||
|   if (!fn) { | ||||
|     events[event!] = undefined | ||||
|     return vm | ||||
|   } | ||||
|   events[event!] = cbs.filter(cb => !(cb === fn || (cb as any).fn === fn)) | ||||
|   return vm | ||||
| } | ||||
| 
 | ||||
| export function emit( | ||||
|   instance: ComponentInternalInstance, | ||||
|   event: string, | ||||
|   ...args: any[] | ||||
| ) { | ||||
|   const cbs = getRegistry(instance)[event] | ||||
|   if (cbs) { | ||||
|     callWithAsyncErrorHandling( | ||||
|       cbs, | ||||
|       instance, | ||||
|       ErrorCodes.COMPONENT_EVENT_HANDLER, | ||||
|       args | ||||
|     ) | ||||
|   } | ||||
|   return instance.proxy | ||||
| } | ||||
| @ -1,6 +1,7 @@ | ||||
| import { extend, NOOP } from '@vue/shared' | ||||
| import { PublicPropertiesMap } from '../componentPublicInstance' | ||||
| import { DeprecationTypes, warnDeprecation } from './deprecations' | ||||
| import { off, on, once } from './eventEmitter' | ||||
| 
 | ||||
| export function installCompatInstanceProperties(map: PublicPropertiesMap) { | ||||
|   const set = (target: any, key: any, val: any) => { | ||||
| @ -29,6 +30,9 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) { | ||||
|       __DEV__ && warnDeprecation(DeprecationTypes.INSTANCE_DESTROY) | ||||
|       // root destroy override from ./global.ts in installCompatMount
 | ||||
|       return i.ctx._compat_destroy || NOOP | ||||
|     } | ||||
|     }, | ||||
|     $on: i => on.bind(null, i), | ||||
|     $once: i => once.bind(null, i), | ||||
|     $off: i => off.bind(null, i) | ||||
|   } as PublicPropertiesMap) | ||||
| } | ||||
|  | ||||
| @ -21,6 +21,7 @@ import { warn } from './warning' | ||||
| import { UnionToIntersection } from './helpers/typeUtils' | ||||
| import { devtoolsComponentEmit } from './devtools' | ||||
| import { AppContext } from './apiCreateApp' | ||||
| import { emit as compatEmit } from './compat/eventEmitter' | ||||
| 
 | ||||
| export type ObjectEmitsOptions = Record< | ||||
|   string, | ||||
| @ -148,6 +149,10 @@ export function emit( | ||||
|       args | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   if (__COMPAT__) { | ||||
|     return compatEmit(instance, event, args) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function normalizeEmitsOptions( | ||||
|  | ||||
| @ -1417,6 +1417,9 @@ function baseCreateRenderer( | ||||
|         if ((vnodeHook = props && props.onVnodeBeforeMount)) { | ||||
|           invokeVNodeHook(vnodeHook, parent, initialVNode) | ||||
|         } | ||||
|         if (__COMPAT__) { | ||||
|           instance.emit('hook:beforeMount') | ||||
|         } | ||||
| 
 | ||||
|         // render
 | ||||
|         if (__DEV__) { | ||||
| @ -1467,19 +1470,29 @@ function baseCreateRenderer( | ||||
|         // onVnodeMounted
 | ||||
|         if ((vnodeHook = props && props.onVnodeMounted)) { | ||||
|           const scopedInitialVNode = initialVNode | ||||
|           queuePostRenderEffect(() => { | ||||
|             invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode) | ||||
|           }, parentSuspense) | ||||
|           queuePostRenderEffect( | ||||
|             () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode), | ||||
|             parentSuspense | ||||
|           ) | ||||
|         } | ||||
|         if (__COMPAT__) { | ||||
|           queuePostRenderEffect( | ||||
|             () => instance.emit('hook:mounted'), | ||||
|             parentSuspense | ||||
|           ) | ||||
|         } | ||||
| 
 | ||||
|         // activated hook for keep-alive roots.
 | ||||
|         // #1742 activated hook must be accessed after first render
 | ||||
|         // since the hook may be injected by a child keep-alive
 | ||||
|         const { a } = instance | ||||
|         if ( | ||||
|           a && | ||||
|           initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE | ||||
|         ) { | ||||
|           queuePostRenderEffect(a, parentSuspense) | ||||
|         if (initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { | ||||
|           instance.a && queuePostRenderEffect(instance.a, parentSuspense) | ||||
|           if (__COMPAT__) { | ||||
|             queuePostRenderEffect( | ||||
|               () => instance.emit('hook:activated'), | ||||
|               parentSuspense | ||||
|             ) | ||||
|           } | ||||
|         } | ||||
|         instance.isMounted = true | ||||
| 
 | ||||
| @ -1515,6 +1528,9 @@ function baseCreateRenderer( | ||||
|         if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) { | ||||
|           invokeVNodeHook(vnodeHook, parent, next, vnode) | ||||
|         } | ||||
|         if (__COMPAT__) { | ||||
|           instance.emit('hook:beforeUpdate') | ||||
|         } | ||||
| 
 | ||||
|         // render
 | ||||
|         if (__DEV__) { | ||||
| @ -1557,9 +1573,16 @@ function baseCreateRenderer( | ||||
|         } | ||||
|         // onVnodeUpdated
 | ||||
|         if ((vnodeHook = next.props && next.props.onVnodeUpdated)) { | ||||
|           queuePostRenderEffect(() => { | ||||
|             invokeVNodeHook(vnodeHook!, parent, next!, vnode) | ||||
|           }, parentSuspense) | ||||
|           queuePostRenderEffect( | ||||
|             () => invokeVNodeHook(vnodeHook!, parent, next!, vnode), | ||||
|             parentSuspense | ||||
|           ) | ||||
|         } | ||||
|         if (__COMPAT__) { | ||||
|           queuePostRenderEffect( | ||||
|             () => instance.emit('hook:updated'), | ||||
|             parentSuspense | ||||
|           ) | ||||
|         } | ||||
| 
 | ||||
|         if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { | ||||
| @ -2211,10 +2234,15 @@ function baseCreateRenderer( | ||||
|     } | ||||
| 
 | ||||
|     const { bum, effects, update, subTree, um } = instance | ||||
| 
 | ||||
|     // beforeUnmount hook
 | ||||
|     if (bum) { | ||||
|       invokeArrayFns(bum) | ||||
|     } | ||||
|     if (__COMPAT__) { | ||||
|       instance.emit('hook:beforeDestroy') | ||||
|     } | ||||
| 
 | ||||
|     if (effects) { | ||||
|       for (let i = 0; i < effects.length; i++) { | ||||
|         stop(effects[i]) | ||||
| @ -2230,6 +2258,12 @@ function baseCreateRenderer( | ||||
|     if (um) { | ||||
|       queuePostRenderEffect(um, parentSuspense) | ||||
|     } | ||||
|     if (__COMPAT__) { | ||||
|       queuePostRenderEffect( | ||||
|         () => instance.emit('hook:destroyed'), | ||||
|         parentSuspense | ||||
|       ) | ||||
|     } | ||||
|     queuePostRenderEffect(() => { | ||||
|       instance.isUnmounted = true | ||||
|     }, parentSuspense) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user