fix(compat): correctly merge lifecycle hooks when using Vue.extend (#3762)
fix #3761
This commit is contained in:
parent
a56ab148fd
commit
2bfb8b574d
@ -113,11 +113,15 @@ export const legacyOptionMergeStrats = {
|
||||
watch: mergeObjectOptions
|
||||
}
|
||||
|
||||
function toArray(target: any) {
|
||||
return isArray(target) ? target : target ? [target] : []
|
||||
}
|
||||
|
||||
function mergeHook(
|
||||
to: Function[] | Function | undefined,
|
||||
from: Function | Function[]
|
||||
) {
|
||||
return Array.from(new Set([...(isArray(to) ? to : to ? [to] : []), from]))
|
||||
return Array.from(new Set([...toArray(to), ...toArray(from)]))
|
||||
}
|
||||
|
||||
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
|
||||
|
@ -767,55 +767,44 @@ export function applyOptions(
|
||||
globalMixins
|
||||
)
|
||||
}
|
||||
if (beforeMount) {
|
||||
onBeforeMount(beforeMount.bind(publicThis))
|
||||
}
|
||||
if (mounted) {
|
||||
onMounted(mounted.bind(publicThis))
|
||||
}
|
||||
if (beforeUpdate) {
|
||||
onBeforeUpdate(beforeUpdate.bind(publicThis))
|
||||
}
|
||||
if (updated) {
|
||||
onUpdated(updated.bind(publicThis))
|
||||
}
|
||||
if (activated) {
|
||||
onActivated(activated.bind(publicThis))
|
||||
}
|
||||
if (deactivated) {
|
||||
onDeactivated(deactivated.bind(publicThis))
|
||||
}
|
||||
if (errorCaptured) {
|
||||
onErrorCaptured(errorCaptured.bind(publicThis))
|
||||
}
|
||||
if (renderTracked) {
|
||||
onRenderTracked(renderTracked.bind(publicThis))
|
||||
}
|
||||
if (renderTriggered) {
|
||||
onRenderTriggered(renderTriggered.bind(publicThis))
|
||||
}
|
||||
if (beforeUnmount) {
|
||||
onBeforeUnmount(beforeUnmount.bind(publicThis))
|
||||
}
|
||||
if (unmounted) {
|
||||
onUnmounted(unmounted.bind(publicThis))
|
||||
}
|
||||
if (serverPrefetch) {
|
||||
onServerPrefetch(serverPrefetch.bind(publicThis))
|
||||
|
||||
function registerLifecycleHook(
|
||||
register: Function,
|
||||
hook?: Function | Function[]
|
||||
) {
|
||||
// Array lifecycle hooks are only present in the compat build
|
||||
if (__COMPAT__ && isArray(hook)) {
|
||||
hook.forEach(_hook => register(_hook.bind(publicThis)))
|
||||
} else if (hook) {
|
||||
register((hook as Function).bind(publicThis))
|
||||
}
|
||||
}
|
||||
|
||||
registerLifecycleHook(onBeforeMount, beforeMount)
|
||||
registerLifecycleHook(onMounted, mounted)
|
||||
registerLifecycleHook(onBeforeUpdate, beforeUpdate)
|
||||
registerLifecycleHook(onUpdated, updated)
|
||||
registerLifecycleHook(onActivated, activated)
|
||||
registerLifecycleHook(onDeactivated, deactivated)
|
||||
registerLifecycleHook(onErrorCaptured, errorCaptured)
|
||||
registerLifecycleHook(onRenderTracked, renderTracked)
|
||||
registerLifecycleHook(onRenderTriggered, renderTriggered)
|
||||
registerLifecycleHook(onBeforeUnmount, beforeUnmount)
|
||||
registerLifecycleHook(onUnmounted, unmounted)
|
||||
registerLifecycleHook(onServerPrefetch, serverPrefetch)
|
||||
|
||||
if (__COMPAT__) {
|
||||
if (
|
||||
beforeDestroy &&
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
|
||||
) {
|
||||
onBeforeUnmount(beforeDestroy.bind(publicThis))
|
||||
registerLifecycleHook(onBeforeUnmount, beforeDestroy)
|
||||
}
|
||||
if (
|
||||
destroyed &&
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED, instance)
|
||||
) {
|
||||
onUnmounted(destroyed.bind(publicThis))
|
||||
registerLifecycleHook(onUnmounted, destroyed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,22 +145,31 @@ describe('GLOBAL_EXTEND', () => {
|
||||
})
|
||||
|
||||
it('should not merge nested mixins created with Vue.extend', () => {
|
||||
const a = jest.fn();
|
||||
const b = jest.fn();
|
||||
const c = jest.fn();
|
||||
const d = jest.fn();
|
||||
const A = Vue.extend({
|
||||
created: () => {}
|
||||
created: a
|
||||
})
|
||||
const B = Vue.extend({
|
||||
mixins: [A],
|
||||
created: () => {}
|
||||
created: b
|
||||
})
|
||||
const C = Vue.extend({
|
||||
extends: B,
|
||||
created: () => {}
|
||||
created: c
|
||||
})
|
||||
const D = Vue.extend({
|
||||
mixins: [C],
|
||||
created: () => {}
|
||||
created: d,
|
||||
render() { return null },
|
||||
})
|
||||
expect(D.options.created!.length).toBe(4)
|
||||
new D().$mount()
|
||||
expect(a.mock.calls.length).toStrictEqual(1)
|
||||
expect(b.mock.calls.length).toStrictEqual(1)
|
||||
expect(c.mock.calls.length).toStrictEqual(1)
|
||||
expect(d.mock.calls.length).toStrictEqual(1)
|
||||
})
|
||||
|
||||
it('should merge methods', () => {
|
||||
|
@ -10,7 +10,8 @@ beforeEach(() => {
|
||||
toggleDeprecationWarning(true)
|
||||
Vue.configureCompat({
|
||||
MODE: 2,
|
||||
GLOBAL_MOUNT: 'suppress-warning'
|
||||
GLOBAL_MOUNT: 'suppress-warning',
|
||||
GLOBAL_EXTEND: 'suppress-warning'
|
||||
})
|
||||
})
|
||||
|
||||
@ -90,3 +91,35 @@ test('beforeDestroy/destroyed', async () => {
|
||||
deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('beforeDestroy/destroyed in Vue.extend components', async () => {
|
||||
const beforeDestroy = jest.fn()
|
||||
const destroyed = jest.fn()
|
||||
|
||||
const child = Vue.extend({
|
||||
template: `foo`,
|
||||
beforeDestroy,
|
||||
destroyed
|
||||
})
|
||||
|
||||
const vm = new Vue({
|
||||
template: `<child v-if="ok"/>`,
|
||||
data() {
|
||||
return { ok: true }
|
||||
},
|
||||
components: { child }
|
||||
}).$mount() as any
|
||||
|
||||
vm.ok = false
|
||||
await nextTick()
|
||||
expect(beforeDestroy).toHaveBeenCalled()
|
||||
expect(destroyed).toHaveBeenCalled()
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.OPTIONS_BEFORE_DESTROY].message
|
||||
).toHaveBeenWarned()
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user