Bläddra i källkod

fix(KeepAlive): use resolved component name for async components in cache pruning (#14212)

close #14210
huangxiuqi 4 månader sedan
förälder
incheckning
dfe667c856

+ 49 - 0
packages/runtime-core/__tests__/components/KeepAlive.spec.ts

@@ -1173,4 +1173,53 @@ describe('KeepAlive', () => {
     expect(deactivatedHome).toHaveBeenCalledTimes(0)
     expect(deactivatedHome).toHaveBeenCalledTimes(0)
     expect(unmountedHome).toHaveBeenCalledTimes(1)
     expect(unmountedHome).toHaveBeenCalledTimes(1)
   })
   })
+
+  test('should work with async component when update `include` props', async () => {
+    let resolve: (comp: Component) => void
+    const AsyncComp = defineAsyncComponent(
+      () =>
+        new Promise(r => {
+          resolve = r as any
+        }),
+    )
+
+    const toggle = ref(true)
+    const instanceRef = ref<any>(null)
+    const keepaliveInclude = ref(['Foo'])
+    const App = {
+      render: () => {
+        return h(KeepAlive, { include: keepaliveInclude.value }, () =>
+          toggle.value ? h(AsyncComp, { ref: instanceRef }) : null,
+        )
+      },
+    }
+
+    render(h(App), root)
+    // async component has not been resolved
+    expect(serializeInner(root)).toBe('<!---->')
+
+    resolve!({
+      name: 'Foo',
+      data: () => ({ count: 0 }),
+      render() {
+        return h('p', this.count)
+      },
+    })
+
+    await timeout()
+    // resolved
+    expect(serializeInner(root)).toBe('<p>0</p>')
+
+    // change state + toggle out + update `include` props
+    instanceRef.value.count++
+    toggle.value = false
+    keepaliveInclude.value = ['Foo']
+    await nextTick()
+    expect(serializeInner(root)).toBe('<!---->')
+
+    // toggle in, state should be maintained
+    toggle.value = true
+    await nextTick()
+    expect(serializeInner(root)).toBe('<p>1</p>')
+  })
 })
 })

+ 7 - 1
packages/runtime-core/src/components/KeepAlive.ts

@@ -202,7 +202,13 @@ const KeepAliveImpl: ComponentOptions = {
 
 
     function pruneCache(filter: (name: string) => boolean) {
     function pruneCache(filter: (name: string) => boolean) {
       cache.forEach((vnode, key) => {
       cache.forEach((vnode, key) => {
-        const name = getComponentName(vnode.type as ConcreteComponent)
+        // for async components, name check should be based in its loaded
+        // inner component if available
+        const name = getComponentName(
+          isAsyncWrapper(vnode)
+            ? (vnode.type as ComponentOptions).__asyncResolved || {}
+            : (vnode.type as ConcreteComponent),
+        )
         if (name && !filter(name)) {
         if (name && !filter(name)) {
           pruneCacheEntry(key)
           pruneCacheEntry(key)
         }
         }