fix(keep-alive): fix keep-alive with scopeId/fallthrough attrs
fix #1511
This commit is contained in:
parent
6dd59ee301
commit
d86b01ba3a
@ -14,7 +14,8 @@ import {
|
|||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
Ref,
|
Ref,
|
||||||
cloneVNode,
|
cloneVNode,
|
||||||
provide
|
provide,
|
||||||
|
withScopeId
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
||||||
|
|
||||||
@ -655,4 +656,30 @@ describe('KeepAlive', () => {
|
|||||||
expect(spyMounted).toHaveBeenCalledTimes(3)
|
expect(spyMounted).toHaveBeenCalledTimes(3)
|
||||||
expect(spyUnmounted).toHaveBeenCalledTimes(4)
|
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>`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,13 @@ import {
|
|||||||
} from '../component'
|
} from '../component'
|
||||||
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
|
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { onBeforeUnmount, injectHook, onUnmounted } from '../apiLifecycle'
|
import {
|
||||||
|
onBeforeUnmount,
|
||||||
|
injectHook,
|
||||||
|
onUnmounted,
|
||||||
|
onBeforeMount,
|
||||||
|
onBeforeUpdate
|
||||||
|
} from '../apiLifecycle'
|
||||||
import {
|
import {
|
||||||
isString,
|
isString,
|
||||||
isArray,
|
isArray,
|
||||||
@ -173,6 +179,16 @@ const KeepAliveImpl = {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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(() => {
|
onBeforeUnmount(() => {
|
||||||
cache.forEach(cached => {
|
cache.forEach(cached => {
|
||||||
const { subTree, suspense } = instance
|
const { subTree, suspense } = instance
|
||||||
@ -189,6 +205,8 @@ const KeepAliveImpl = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
pendingCacheKey = null
|
||||||
|
|
||||||
if (!slots.default) {
|
if (!slots.default) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -227,7 +245,12 @@ const KeepAliveImpl = {
|
|||||||
if (vnode.el) {
|
if (vnode.el) {
|
||||||
vnode = cloneVNode(vnode)
|
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) {
|
if (cachedVNode) {
|
||||||
// copy over mounted state
|
// copy over mounted state
|
||||||
|
Loading…
Reference in New Issue
Block a user