KeepAlive,
serializeInner,
nextTick,
- ComponentOptions
+ ComponentOptions,
+ markRaw,
+ inject,
+ defineComponent,
+ ComponentPublicInstance,
+ Ref,
+ cloneVNode,
+ provide
} from '@vue/runtime-test'
import { KeepAliveProps } from '../../src/components/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)
+ })
})
) => {
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
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)
}
}
- if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {
+ if (
+ ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) &&
+ !shouldKeepAlive
+ ) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
shouldInvokeDirs &&