wip: custom directive compat

This commit is contained in:
Evan You 2021-04-06 11:57:10 -04:00
parent d619a770a8
commit 47b765d63a
4 changed files with 74 additions and 5 deletions

View File

@ -0,0 +1,49 @@
import { isArray } from '@vue/shared/src'
import { ObjectDirective, DirectiveHook } from '../directives'
import { DeprecationTypes, warnDeprecation } from './deprecations'
export interface LegacyDirective {
bind?: DirectiveHook
inserted?: DirectiveHook
update?: DirectiveHook
componentUpdated?: DirectiveHook
unbind?: DirectiveHook
}
const legacyDirectiveHookMap: Partial<
Record<
keyof ObjectDirective,
keyof LegacyDirective | (keyof LegacyDirective)[]
>
> = {
beforeMount: 'bind',
mounted: 'inserted',
updated: ['update', 'componentUpdated'],
unmounted: 'unbind'
}
export function mapCompatDirectiveHook(
name: keyof ObjectDirective,
dir: ObjectDirective & LegacyDirective
): DirectiveHook | DirectiveHook[] | undefined {
const mappedName = legacyDirectiveHookMap[name]
if (mappedName) {
if (isArray(mappedName)) {
const hook: DirectiveHook[] = []
mappedName.forEach(name => {
const mappedHook = dir[name]
if (mappedHook) {
__DEV__ &&
warnDeprecation(DeprecationTypes.CUSTOM_DIR, mappedName, name)
hook.push(mappedHook)
}
})
return hook.length ? hook : undefined
} else {
if (__DEV__ && dir[mappedName]) {
warnDeprecation(DeprecationTypes.CUSTOM_DIR, mappedName, name)
}
return dir[mappedName]
}
}
}

View File

@ -24,7 +24,9 @@ export const enum DeprecationTypes {
OPTIONS_BEFORE_DESTROY, OPTIONS_BEFORE_DESTROY,
OPTIONS_DESTROYED, OPTIONS_DESTROYED,
PROPS_DEFAULT_THIS PROPS_DEFAULT_THIS,
CUSTOM_DIR
} }
type DeprecationData = { type DeprecationData = {
@ -157,6 +159,13 @@ const deprecations: Record<DeprecationTypes, DeprecationData> = {
`props default value function no longer has access to "this". ` + `props default value function no longer has access to "this". ` +
`(found in prop "${key}")`, `(found in prop "${key}")`,
link: `https://v3.vuejs.org/guide/migration/props-default-this.html` link: `https://v3.vuejs.org/guide/migration/props-default-this.html`
},
[DeprecationTypes.CUSTOM_DIR]: {
message: (legacyHook: string, newHook: string) =>
`Custom directive hook "${legacyHook}" has been removed. ` +
`Use "${newHook}" instead.`,
link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
} }
} }

View File

@ -28,6 +28,7 @@ import { nextTick } from '../scheduler'
import { warnDeprecation, DeprecationTypes } from './deprecations' import { warnDeprecation, DeprecationTypes } from './deprecations'
import { version } from '..' import { version } from '..'
import { LegacyConfig } from './globalConfig' import { LegacyConfig } from './globalConfig'
import { LegacyDirective } from './customDirective'
/** /**
* @deprecated the default `Vue` export has been removed in Vue 3. The type for * @deprecated the default `Vue` export has been removed in Vue 3. The type for
@ -94,6 +95,12 @@ export function createCompatVue(
function createCompatApp(options: ComponentOptions = {}, Ctor: any) { function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
const app = createApp(options) const app = createApp(options)
// copy over asset registries and deopt flag
;['mixins', 'components', 'directives', 'deopt'].forEach(key => {
// @ts-ignore
app._context[key] = singletonApp._context[key]
})
// copy over global config mutations // copy over global config mutations
isCopyingConfig = true isCopyingConfig = true
for (const key in singletonApp.config) { for (const key in singletonApp.config) {
@ -184,7 +191,7 @@ export function createCompatVue(
return Vue return Vue
} }
Vue.component = ((name: string, comp: any) => { Vue.component = ((name: string, comp: Component) => {
if (comp) { if (comp) {
singletonApp.component(name, comp) singletonApp.component(name, comp)
return Vue return Vue
@ -193,9 +200,9 @@ export function createCompatVue(
} }
}) as any }) as any
Vue.directive = ((name: string, dir: any) => { Vue.directive = ((name: string, dir: Directive | LegacyDirective) => {
if (dir) { if (dir) {
singletonApp.directive(name, dir) singletonApp.directive(name, dir as Directive)
return Vue return Vue
} else { } else {
return singletonApp.directive(name) return singletonApp.directive(name)

View File

@ -18,6 +18,7 @@ import { ComponentInternalInstance, Data } from './component'
import { currentRenderingInstance } from './componentRenderContext' import { currentRenderingInstance } from './componentRenderContext'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { ComponentPublicInstance } from './componentPublicInstance' import { ComponentPublicInstance } from './componentPublicInstance'
import { mapCompatDirectiveHook } from './compat/customDirective'
export interface DirectiveBinding<V = any> { export interface DirectiveBinding<V = any> {
instance: ComponentPublicInstance | null instance: ComponentPublicInstance | null
@ -124,7 +125,10 @@ export function invokeDirectiveHook(
if (oldBindings) { if (oldBindings) {
binding.oldValue = oldBindings[i].value binding.oldValue = oldBindings[i].value
} }
const hook = binding.dir[name] as DirectiveHook | undefined let hook = binding.dir[name] as DirectiveHook | DirectiveHook[] | undefined
if (__COMPAT__ && !hook) {
hook = mapCompatDirectiveHook(name, binding.dir)
}
if (hook) { if (hook) {
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [ callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
vnode.el, vnode.el,