]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(keep-alive): fix unmounting late-included components
authorEvan You <yyx990803@gmail.com>
Thu, 12 May 2022 03:52:54 +0000 (11:52 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 12 May 2022 03:52:54 +0000 (11:52 +0800)
fix #3648
based on #3650

packages/runtime-core/__tests__/components/KeepAlive.spec.ts
packages/runtime-core/src/components/KeepAlive.ts

index b60931e1ffb1110e9c7df6c6b3b77d0f5a1f3bfa..79e7811723a621b9b0eaa58223db037068ab2f79 100644 (file)
@@ -18,7 +18,12 @@ import {
   defineAsyncComponent,
   Component,
   createApp,
-  onActivated
+  onActivated,
+  onUnmounted,
+  onMounted,
+  reactive,
+  shallowRef,
+  onDeactivated
 } from '@vue/runtime-test'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 
@@ -903,4 +908,73 @@ describe('KeepAlive', () => {
     await nextTick()
     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)
+  })
 })
index 15dbff5291382504790d2129052297d9b8fbfaa9..4b2c7288581ef43b4ce58297cc4c7a0bf60c4f4e 100644 (file)
@@ -42,6 +42,7 @@ import { setTransitionHooks } from './BaseTransition'
 import { ComponentRenderContext } from '../componentPublicInstance'
 import { devtoolsComponentAdded } from '../devtools'
 import { isAsyncWrapper } from '../apiAsyncComponent'
+import { isSuspense } from './Suspense'
 
 type MatchPattern = string | RegExp | (string | RegExp)[]
 
@@ -323,7 +324,7 @@ const KeepAliveImpl: ComponentOptions = {
       vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
 
       current = vnode
-      return rawVNode
+      return isSuspense(rawVNode.type) ? rawVNode : vnode
     }
   }
 }