ComponentPublicInstance,
Ref,
cloneVNode,
- provide
+ provide,
+ withScopeId
} from '@vue/runtime-test'
import { KeepAliveProps } from '../../src/components/KeepAlive'
expect(spyMounted).toHaveBeenCalledTimes(3)
expect(spyUnmounted).toHaveBeenCalledTimes(4)
})
+
+ // #1513
+ test('should work with cloned root due to scopeId / fallthrough attrs', async () => {
+ const viewRef = ref('one')
+ const instanceRef = ref<any>(null)
+ const withId = withScopeId('foo')
+ const App = {
+ __scopeId: 'foo',
+ render: withId(() => {
+ return h(KeepAlive, null, {
+ default: () => h(views[viewRef.value], { ref: instanceRef })
+ })
+ })
+ }
+ render(h(App), root)
+ expect(serializeInner(root)).toBe(`<div foo>one</div>`)
+ instanceRef.value.msg = 'changed'
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div foo>changed</div>`)
+ viewRef.value = 'two'
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div foo>two</div>`)
+ viewRef.value = 'one'
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div foo>changed</div>`)
+ })
})
} from '../component'
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
import { warn } from '../warning'
-import { onBeforeUnmount, injectHook, onUnmounted } from '../apiLifecycle'
+import {
+ onBeforeUnmount,
+ injectHook,
+ onUnmounted,
+ onBeforeMount,
+ onBeforeUpdate
+} from '../apiLifecycle'
import {
isString,
isArray,
}
)
+ // cache sub tree in beforeMount/Update (i.e. right after the render)
+ let pendingCacheKey: CacheKey | null = null
+ const cacheSubtree = () => {
+ if (pendingCacheKey) {
+ cache.set(pendingCacheKey, instance.subTree)
+ }
+ }
+ onBeforeMount(cacheSubtree)
+ onBeforeUpdate(cacheSubtree)
+
onBeforeUnmount(() => {
cache.forEach(cached => {
const { subTree, suspense } = instance
})
return () => {
+ pendingCacheKey = null
+
if (!slots.default) {
return null
}
if (vnode.el) {
vnode = cloneVNode(vnode)
}
- cache.set(key, vnode)
+ // #1513 it's possible for the returned vnode to be cloned due to attr
+ // fallthrough or scopeId, so the vnode here may not be the final vnode
+ // that is mounted. Instead of caching it directly, we store the pending
+ // key and cache `instance.subTree` (the normalized vnode) in
+ // beforeMount/beforeUpdate hooks.
+ pendingCacheKey = key
if (cachedVNode) {
// copy over mounted state