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…
Reference in New Issue
Block a user