From f1d1cdbb699e27ac6a0d241209f7e8f8d9ebf2c7 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 12 May 2022 08:19:11 +0800 Subject: [PATCH] fix(runtime-core): fix activated hook when using async component with KeepAlive (#5459) fix #5095 fix #5651 --- .../__tests__/apiAsyncComponent.spec.ts | 38 ++++++++++++++++++- .../runtime-core/src/apiAsyncComponent.ts | 8 +++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts b/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts index b771ba9652d..c9ab33ce917 100644 --- a/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts +++ b/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts @@ -4,9 +4,11 @@ import { Component, ref, nextTick, - Suspense + Suspense, + KeepAlive } from '../src' import { createApp, nodeOps, serializeInner } from '@vue/runtime-test' +import { onActivated } from '../src/components/KeepAlive' const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n)) @@ -799,4 +801,38 @@ describe('api: defineAsyncComponent', () => { expect(vnodeHooks.onVnodeBeforeUnmount).toHaveBeenCalledTimes(1) expect(vnodeHooks.onVnodeUnmounted).toHaveBeenCalledTimes(1) }) + + test('with keepalive', async () => { + const spy = jest.fn() + let resolve: (comp: Component) => void + + const Foo = defineAsyncComponent( + () => + new Promise(r => { + resolve = r as any + }) + ) + + const root = nodeOps.createElement('div') + const app = createApp({ + render: () => h(KeepAlive, [h(Foo)]) + }) + + app.mount(root) + await nextTick() + + resolve!({ + setup() { + onActivated(() => { + spy() + }) + return () => 'resolved' + } + }) + + await timeout() + expect(serializeInner(root)).toBe('resolved') + expect(spy).toBeCalledTimes(1) + }) + }) diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index dc92b90d722..957aa0bb5f0 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -6,7 +6,7 @@ import { isInSSRComponentSetup, ComponentOptions } from './component' -import { isFunction, isObject } from '@vue/shared' +import { isFunction, isObject, ShapeFlags } from '@vue/shared' import { ComponentPublicInstance } from './componentPublicInstance' import { createVNode, VNode } from './vnode' import { defineComponent } from './apiDefineComponent' @@ -211,10 +211,14 @@ export function defineAsyncComponent< function createInnerComp( comp: ConcreteComponent, - { vnode: { ref, props, children } }: ComponentInternalInstance + { vnode: { ref, props, children }, parent }: ComponentInternalInstance ) { const vnode = createVNode(comp, props, children) // ensure inner component inherits the async wrapper's ref owner vnode.ref = ref + + if (parent && isKeepAlive(parent.vnode)) { + vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } return vnode }