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
|
watch: mergeObjectOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toArray(target: any) {
|
||||||
|
return isArray(target) ? target : target ? [target] : []
|
||||||
|
}
|
||||||
|
|
||||||
function mergeHook(
|
function mergeHook(
|
||||||
to: Function[] | Function | undefined,
|
to: Function[] | Function | undefined,
|
||||||
from: Function | Function[]
|
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) {
|
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
|
||||||
|
@ -767,55 +767,44 @@ export function applyOptions(
|
|||||||
globalMixins
|
globalMixins
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (beforeMount) {
|
|
||||||
onBeforeMount(beforeMount.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))
|
||||||
}
|
}
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (__COMPAT__) {
|
||||||
if (
|
if (
|
||||||
beforeDestroy &&
|
beforeDestroy &&
|
||||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
|
softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
|
||||||
) {
|
) {
|
||||||
onBeforeUnmount(beforeDestroy.bind(publicThis))
|
registerLifecycleHook(onBeforeUnmount, beforeDestroy)
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
destroyed &&
|
destroyed &&
|
||||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED, instance)
|
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', () => {
|
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({
|
const A = Vue.extend({
|
||||||
created: () => {}
|
created: a
|
||||||
})
|
})
|
||||||
const B = Vue.extend({
|
const B = Vue.extend({
|
||||||
mixins: [A],
|
mixins: [A],
|
||||||
created: () => {}
|
created: b
|
||||||
})
|
})
|
||||||
const C = Vue.extend({
|
const C = Vue.extend({
|
||||||
extends: B,
|
extends: B,
|
||||||
created: () => {}
|
created: c
|
||||||
})
|
})
|
||||||
const D = Vue.extend({
|
const D = Vue.extend({
|
||||||
mixins: [C],
|
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', () => {
|
it('should merge methods', () => {
|
||||||
|
@ -10,7 +10,8 @@ beforeEach(() => {
|
|||||||
toggleDeprecationWarning(true)
|
toggleDeprecationWarning(true)
|
||||||
Vue.configureCompat({
|
Vue.configureCompat({
|
||||||
MODE: 2,
|
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
|
deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message
|
||||||
).toHaveBeenWarned()
|
).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…
x
Reference in New Issue
Block a user