]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(keep-alive): do not invoke onVnodeBeforeUnmount if is KeepAlive component (#1079)
authorCarlos Rodrigues <carlos@hypermob.co.uk>
Thu, 30 Apr 2020 18:52:03 +0000 (19:52 +0100)
committerGitHub <noreply@github.com>
Thu, 30 Apr 2020 18:52:03 +0000 (14:52 -0400)
packages/runtime-core/__tests__/components/KeepAlive.spec.ts
packages/runtime-core/src/renderer.ts

index be17b0772f64fb45a0c6bf65e98d0acda023f038..bf901f6b1e6ec9b0c22ba3c79b848e9cfc9ff648 100644 (file)
@@ -7,7 +7,14 @@ import {
   KeepAlive,
   serializeInner,
   nextTick,
-  ComponentOptions
+  ComponentOptions,
+  markRaw,
+  inject,
+  defineComponent,
+  ComponentPublicInstance,
+  Ref,
+  cloneVNode,
+  provide
 } from '@vue/runtime-test'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 
@@ -559,4 +566,91 @@ describe('KeepAlive', () => {
       expect(serializeInner(root)).toBe(`1`)
     })
   })
+
+  it('should not call onVnodeUnmounted', async () => {
+    const Foo = markRaw({
+      name: 'Foo',
+      render() {
+        return h('Foo')
+      }
+    })
+    const Bar = markRaw({
+      name: 'Bar',
+      render() {
+        return h('Bar')
+      }
+    })
+
+    const spyMounted = jest.fn()
+    const spyUnmounted = jest.fn()
+
+    const RouterView = defineComponent({
+      setup(_, { slots }) {
+        const Component = inject<Ref<ComponentPublicInstance>>('component')
+        const refView = ref()
+
+        let componentProps = {
+          ref: refView,
+          onVnodeMounted() {
+            spyMounted()
+          },
+          onVnodeUnmounted() {
+            spyUnmounted()
+          }
+        }
+
+        return () => {
+          const child: any = slots.default!({
+            Component: Component!.value
+          })[0]
+
+          const innerChild = child.children[0]
+          child.children[0] = cloneVNode(innerChild, componentProps)
+          return child
+        }
+      }
+    })
+
+    let toggle: () => void = () => {}
+
+    const App = defineComponent({
+      setup() {
+        const component = ref(Foo)
+
+        provide('component', component)
+
+        toggle = () => {
+          component.value = component.value === Foo ? Bar : Foo
+        }
+        return {
+          component,
+          toggle
+        }
+      },
+      render() {
+        return h(RouterView, null, {
+          default: ({ Component }: any) => h(KeepAlive, null, [h(Component)])
+        })
+      }
+    })
+
+    render(h(App), root)
+    await nextTick()
+    expect(spyMounted).toHaveBeenCalledTimes(1)
+    expect(spyUnmounted).toHaveBeenCalledTimes(0)
+
+    toggle()
+    await nextTick()
+
+    expect(spyMounted).toHaveBeenCalledTimes(2)
+    expect(spyUnmounted).toHaveBeenCalledTimes(0)
+
+    toggle()
+    await nextTick()
+    render(null, root)
+    await nextTick()
+
+    expect(spyMounted).toHaveBeenCalledTimes(2)
+    expect(spyUnmounted).toHaveBeenCalledTimes(2)
+  })
 })
index 1891131caeaa45a1774bc4aee982152f120a59d7..fe523cd8f97b4210652a040b06737c26d789ffc5 100644 (file)
@@ -1713,6 +1713,7 @@ function baseCreateRenderer(
   ) => {
     const { props, ref, children, dynamicChildren, shapeFlag, dirs } = vnode
     const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
+    const shouldKeepAlive = shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
     let vnodeHook: VNodeHook | undefined | null
 
     // unset ref
@@ -1720,12 +1721,12 @@ function baseCreateRenderer(
       setRef(ref, null, parentComponent, null)
     }
 
-    if ((vnodeHook = props && props.onVnodeBeforeUnmount)) {
+    if ((vnodeHook = props && props.onVnodeBeforeUnmount) && !shouldKeepAlive) {
       invokeVNodeHook(vnodeHook, parentComponent, vnode)
     }
 
     if (shapeFlag & ShapeFlags.COMPONENT) {
-      if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
+      if (shouldKeepAlive) {
         ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
       } else {
         unmountComponent(vnode.component!, parentSuspense, doRemove)
@@ -1757,7 +1758,10 @@ function baseCreateRenderer(
       }
     }
 
-    if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {
+    if (
+      ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) &&
+      !shouldKeepAlive
+    ) {
       queuePostRenderEffect(() => {
         vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
         shouldInvokeDirs &&