Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3b2ffb7

Browse files
committed
fix(keep-alive): prevent stale cacheBlock() calls when VDOM async component resolves after being unmounted or witched
1 parent 6123f9b commit 3b2ffb7

2 files changed

Lines changed: 75 additions & 3 deletions

File tree

‎packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,6 +2056,71 @@ describe('VaporKeepAlive', () => {
20562056

20572057
expect(container.innerHTML).toContain('async inner')
20582058
})
2059+
2060+
test('should not mis-cache when interop async resolves after switching away', async () => {
2061+
const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
2062+
2063+
let resolveA: (comp: any) => void
2064+
const AsyncCompA = defineAsyncComponent(
2065+
() =>
2066+
new Promise(r => {
2067+
resolveA = r
2068+
}),
2069+
)
2070+
2071+
const InnerCompA = {
2072+
name: 'CompA',
2073+
setup() {
2074+
return () => h('div', 'comp A')
2075+
},
2076+
}
2077+
2078+
const CompB = {
2079+
name: 'CompB',
2080+
setup() {
2081+
return () => h('div', 'comp B')
2082+
},
2083+
}
2084+
2085+
const showA = ref(true)
2086+
let cache: Map<any, any>
2087+
2088+
const App = defineVaporComponent({
2089+
setup() {
2090+
const ka = createComponent(VaporKeepAlive, null, {
2091+
default: () =>
2092+
createIf(
2093+
() => showA.value,
2094+
() => createComponent(AsyncCompA as any),
2095+
() => createComponent(CompB),
2096+
),
2097+
})
2098+
cache = (ka as any).__v_cache
2099+
return ka
2100+
},
2101+
})
2102+
2103+
const container = document.createElement('div')
2104+
document.body.appendChild(container)
2105+
const app = createVaporApp(App)
2106+
app.use(vaporInteropPlugin)
2107+
app.mount(container)
2108+
2109+
// switch to CompB before AsyncCompA resolves
2110+
showA.value = false
2111+
await nextTick()
2112+
expect(container.innerHTML).toContain('comp B')
2113+
2114+
const cacheBeforeResolve = cache!.size
2115+
2116+
// resolve A after switching away — should NOT trigger mis-cache
2117+
resolveA!(InnerCompA)
2118+
await timeout()
2119+
await nextTick()
2120+
2121+
// cache should not have grown from the stale resolution
2122+
expect(cache!.size).toBe(cacheBeforeResolve)
2123+
})
20592124
})
20602125

20612126
test('should invalidate pending mount/activated hooks when deactivated before post flush', async () => {

‎packages/runtime-vapor/src/vdomInterop.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
} from './block'
6868
import {
6969
EMPTY_OBJ,
70+
NOOP,
7071
ShapeFlags,
7172
extend,
7273
isArray,
@@ -626,9 +627,15 @@ function createVDOMComponent(
626627
// for VDOM async components, trigger cacheBlock after resolution
627628
if ((component as any).__asyncLoader) {
628629
const keepAliveCtx = currentKeepAliveCtx
629-
;(component as any).__asyncLoader().then(() => {
630-
keepAliveCtx.cacheBlock()
631-
})
630+
// guard against stale resolution after unmount or branch switch
631+
let disposed = false
632+
onScopeDispose(() => (disposed = true))
633+
;(component as any)
634+
.__asyncLoader()
635+
.then(() => {
636+
if (!disposed) keepAliveCtx.cacheBlock()
637+
})
638+
.catch(NOOP)
632639
}
633640
setCurrentKeepAliveCtx(null)
634641
}

0 commit comments

Comments
 (0)