fix(keep-alive): include/exclude should work with async component (#3531)
fix #3529
This commit is contained in:
parent
f1f52300e6
commit
9e3708ca75
@ -14,10 +14,14 @@ import {
|
|||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
Ref,
|
Ref,
|
||||||
cloneVNode,
|
cloneVNode,
|
||||||
provide
|
provide,
|
||||||
|
defineAsyncComponent,
|
||||||
|
Component
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
||||||
|
|
||||||
|
const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
|
||||||
|
|
||||||
describe('KeepAlive', () => {
|
describe('KeepAlive', () => {
|
||||||
let one: ComponentOptions
|
let one: ComponentOptions
|
||||||
let two: ComponentOptions
|
let two: ComponentOptions
|
||||||
@ -823,4 +827,53 @@ describe('KeepAlive', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe(`<div foo>changed</div>`)
|
expect(serializeInner(root)).toBe(`<div foo>changed</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should work with async component', async () => {
|
||||||
|
let resolve: (comp: Component) => void
|
||||||
|
const AsyncComp = defineAsyncComponent(
|
||||||
|
() =>
|
||||||
|
new Promise(r => {
|
||||||
|
resolve = r as any
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggle = ref(true)
|
||||||
|
const instanceRef = ref<any>(null)
|
||||||
|
const App = {
|
||||||
|
render: () => {
|
||||||
|
return h(
|
||||||
|
KeepAlive,
|
||||||
|
{ include: 'Foo' },
|
||||||
|
() => (toggle.value ? h(AsyncComp, { ref: instanceRef }) : null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(h(App), root)
|
||||||
|
// async component has not been resolved
|
||||||
|
expect(serializeInner(root)).toBe('<!---->')
|
||||||
|
|
||||||
|
resolve!({
|
||||||
|
name: 'Foo',
|
||||||
|
data: () => ({ count: 0 }),
|
||||||
|
render() {
|
||||||
|
return h('p', this.count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await timeout()
|
||||||
|
// resolved
|
||||||
|
expect(serializeInner(root)).toBe('<p>0</p>')
|
||||||
|
|
||||||
|
// change state + toggle out
|
||||||
|
instanceRef.value.count++
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe('<!---->')
|
||||||
|
|
||||||
|
// toggle in, state should be maintained
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe('<p>1</p>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -13,6 +13,8 @@ import { defineComponent } from './apiDefineComponent'
|
|||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { ref } from '@vue/reactivity'
|
import { ref } from '@vue/reactivity'
|
||||||
import { handleError, ErrorCodes } from './errorHandling'
|
import { handleError, ErrorCodes } from './errorHandling'
|
||||||
|
import { isKeepAlive } from './components/KeepAlive'
|
||||||
|
import { queueJob } from './scheduler'
|
||||||
|
|
||||||
export type AsyncComponentResolveResult<T = Component> = T | { default: T } // es modules
|
export type AsyncComponentResolveResult<T = Component> = T | { default: T } // es modules
|
||||||
|
|
||||||
@ -109,8 +111,14 @@ export function defineAsyncComponent<
|
|||||||
}
|
}
|
||||||
|
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
__asyncLoader: load,
|
|
||||||
name: 'AsyncComponentWrapper',
|
name: 'AsyncComponentWrapper',
|
||||||
|
|
||||||
|
__asyncLoader: load,
|
||||||
|
|
||||||
|
get __asyncResolved() {
|
||||||
|
return resolvedComp
|
||||||
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const instance = currentInstance!
|
const instance = currentInstance!
|
||||||
|
|
||||||
@ -174,6 +182,11 @@ export function defineAsyncComponent<
|
|||||||
load()
|
load()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
|
if (instance.parent && isKeepAlive(instance.parent.vnode)) {
|
||||||
|
// parent is keep-alive, force update so the loaded component's
|
||||||
|
// name is taken into account
|
||||||
|
queueJob(instance.parent.update)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
onError(err)
|
onError(err)
|
||||||
|
@ -189,6 +189,11 @@ export interface ComponentOptionsBase<
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
__asyncLoader?: () => Promise<ConcreteComponent>
|
__asyncLoader?: () => Promise<ConcreteComponent>
|
||||||
|
/**
|
||||||
|
* the inner component resolved by the AsyncComponentWrapper
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
__asyncResolved?: ConcreteComponent
|
||||||
/**
|
/**
|
||||||
* cache for merged $options
|
* cache for merged $options
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
import { setTransitionHooks } from './BaseTransition'
|
import { setTransitionHooks } from './BaseTransition'
|
||||||
import { ComponentRenderContext } from '../componentPublicInstance'
|
import { ComponentRenderContext } from '../componentPublicInstance'
|
||||||
import { devtoolsComponentAdded } from '../devtools'
|
import { devtoolsComponentAdded } from '../devtools'
|
||||||
|
import { isAsyncWrapper } from '../apiAsyncComponent'
|
||||||
|
|
||||||
type MatchPattern = string | RegExp | string[] | RegExp[]
|
type MatchPattern = string | RegExp | string[] | RegExp[]
|
||||||
|
|
||||||
@ -257,7 +258,15 @@ const KeepAliveImpl: ComponentOptions = {
|
|||||||
|
|
||||||
let vnode = getInnerChild(rawVNode)
|
let vnode = getInnerChild(rawVNode)
|
||||||
const comp = vnode.type as ConcreteComponent
|
const comp = vnode.type as ConcreteComponent
|
||||||
const name = getComponentName(comp)
|
|
||||||
|
// for async components, name check should be based in its loaded
|
||||||
|
// inner component if available
|
||||||
|
const name = getComponentName(
|
||||||
|
isAsyncWrapper(vnode)
|
||||||
|
? (vnode.type as ComponentOptions).__asyncResolved || {}
|
||||||
|
: comp
|
||||||
|
)
|
||||||
|
|
||||||
const { include, exclude, max } = props
|
const { include, exclude, max } = props
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
Loading…
Reference in New Issue
Block a user