Просмотр исходного кода

fix(keep-alive): fix unmounting late-included components

fix #3648
based on #3650
Evan You 4 лет назад
Родитель
Сommit
da49c863a2

+ 75 - 1
packages/runtime-core/__tests__/components/KeepAlive.spec.ts

@@ -18,7 +18,12 @@ import {
   defineAsyncComponent,
   defineAsyncComponent,
   Component,
   Component,
   createApp,
   createApp,
-  onActivated
+  onActivated,
+  onUnmounted,
+  onMounted,
+  reactive,
+  shallowRef,
+  onDeactivated
 } from '@vue/runtime-test'
 } from '@vue/runtime-test'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 
 
@@ -903,4 +908,73 @@ describe('KeepAlive', () => {
     await nextTick()
     await nextTick()
     expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
     expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
   })
   })
+
+  // #3648
+  test('should avoid unmount later included components', async () => {
+    const unmountedA = jest.fn()
+    const mountedA = jest.fn()
+    const activatedA = jest.fn()
+    const deactivatedA = jest.fn()
+    const unmountedB = jest.fn()
+    const mountedB = jest.fn()
+
+    const A = {
+      name: 'A',
+      setup() {
+        onMounted(mountedA)
+        onUnmounted(unmountedA)
+        onActivated(activatedA)
+        onDeactivated(deactivatedA)
+        return () => 'A'
+      }
+    }
+    const B = {
+      name: 'B',
+      setup() {
+        onMounted(mountedB)
+        onUnmounted(unmountedB)
+        return () => 'B'
+      }
+    }
+
+    const include = reactive<string[]>([])
+    const current = shallowRef(A)
+    const app = createApp({
+      setup() {
+        return () => {
+          return [
+            h(
+              KeepAlive,
+              {
+                include
+              },
+              h(current.value)
+            )
+          ]
+        }
+      }
+    })
+
+    app.mount(root)
+
+    expect(serializeInner(root)).toBe(`A`)
+    expect(mountedA).toHaveBeenCalledTimes(1)
+    expect(unmountedA).toHaveBeenCalledTimes(0)
+    expect(activatedA).toHaveBeenCalledTimes(0)
+    expect(deactivatedA).toHaveBeenCalledTimes(0)
+    expect(mountedB).toHaveBeenCalledTimes(0)
+    expect(unmountedB).toHaveBeenCalledTimes(0)
+
+    include.push('A') // cache A
+    await nextTick()
+    current.value = B // toggle to B
+    await nextTick()
+    expect(serializeInner(root)).toBe(`B`)
+    expect(mountedA).toHaveBeenCalledTimes(1)
+    expect(unmountedA).toHaveBeenCalledTimes(0)
+    expect(activatedA).toHaveBeenCalledTimes(0)
+    expect(deactivatedA).toHaveBeenCalledTimes(1)
+    expect(mountedB).toHaveBeenCalledTimes(1)
+    expect(unmountedB).toHaveBeenCalledTimes(0)
+  })
 })
 })

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

@@ -42,6 +42,7 @@ import { setTransitionHooks } from './BaseTransition'
 import { ComponentRenderContext } from '../componentPublicInstance'
 import { ComponentRenderContext } from '../componentPublicInstance'
 import { devtoolsComponentAdded } from '../devtools'
 import { devtoolsComponentAdded } from '../devtools'
 import { isAsyncWrapper } from '../apiAsyncComponent'
 import { isAsyncWrapper } from '../apiAsyncComponent'
+import { isSuspense } from './Suspense'
 
 
 type MatchPattern = string | RegExp | (string | RegExp)[]
 type MatchPattern = string | RegExp | (string | RegExp)[]
 
 
@@ -323,7 +324,7 @@ const KeepAliveImpl: ComponentOptions = {
       vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
       vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
 
 
       current = vnode
       current = vnode
-      return rawVNode
+      return isSuspense(rawVNode.type) ? rawVNode : vnode
     }
     }
   }
   }
 }
 }