Skip to content

Commit 7f29943

Browse files
authored
fix(runtime-core): ensure correct anchor el for unresolved async components (#13560)
close #13559
1 parent 9b02923 commit 7f29943

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

packages/runtime-core/__tests__/components/Suspense.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,5 +2230,57 @@ describe('Suspense', () => {
22302230
fallback: [h('div'), h('div')],
22312231
})
22322232
})
2233+
2234+
// #13559
2235+
test('renders multiple async components in Suspense with v-for and updates on items change', async () => {
2236+
const CompAsyncSetup = defineAsyncComponent({
2237+
props: ['item'],
2238+
render(ctx: any) {
2239+
return h('div', ctx.item.name)
2240+
},
2241+
})
2242+
2243+
const items = ref([
2244+
{ id: 1, name: '111' },
2245+
{ id: 2, name: '222' },
2246+
{ id: 3, name: '333' },
2247+
])
2248+
2249+
const Comp = {
2250+
setup() {
2251+
return () =>
2252+
h(Suspense, null, {
2253+
default: () =>
2254+
h(
2255+
Fragment,
2256+
null,
2257+
items.value.map(item =>
2258+
h(CompAsyncSetup, { item, key: item.id }),
2259+
),
2260+
),
2261+
})
2262+
},
2263+
}
2264+
2265+
const root = nodeOps.createElement('div')
2266+
render(h(Comp), root)
2267+
await nextTick()
2268+
await Promise.all(deps)
2269+
2270+
expect(serializeInner(root)).toBe(
2271+
`<div>111</div><div>222</div><div>333</div>`,
2272+
)
2273+
2274+
items.value = [
2275+
{ id: 4, name: '444' },
2276+
{ id: 5, name: '555' },
2277+
{ id: 6, name: '666' },
2278+
]
2279+
await nextTick()
2280+
await Promise.all(deps)
2281+
expect(serializeInner(root)).toBe(
2282+
`<div>444</div><div>555</div><div>666</div>`,
2283+
)
2284+
})
22332285
})
22342286
})

packages/runtime-core/src/renderer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,7 @@ function baseCreateRenderer(
12261226
if (!initialVNode.el) {
12271227
const placeholder = (instance.subTree = createVNode(Comment))
12281228
processCommentNode(null, placeholder, container!, anchor)
1229+
initialVNode.placeholder = placeholder.el
12291230
}
12301231
} else {
12311232
setupRenderEffect(
@@ -1979,8 +1980,12 @@ function baseCreateRenderer(
19791980
for (i = toBePatched - 1; i >= 0; i--) {
19801981
const nextIndex = s2 + i
19811982
const nextChild = c2[nextIndex] as VNode
1983+
const anchorVNode = c2[nextIndex + 1] as VNode
19821984
const anchor =
1983-
nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
1985+
nextIndex + 1 < l2
1986+
? // #13559, fallback to el placeholder for unresolved async component
1987+
anchorVNode.el || anchorVNode.placeholder
1988+
: parentAnchor
19841989
if (newIndexToOldIndexMap[i] === 0) {
19851990
// mount new
19861991
patch(

packages/runtime-core/src/vnode.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ export interface VNode<
196196

197197
// DOM
198198
el: HostNode | null
199+
placeholder: HostNode | null // async component el placeholder
199200
anchor: HostNode | null // fragment anchor
200201
target: HostElement | null // teleport target
201202
targetStart: HostNode | null // teleport target start anchor
@@ -711,6 +712,8 @@ export function cloneVNode<T, U>(
711712
suspense: vnode.suspense,
712713
ssContent: vnode.ssContent && cloneVNode(vnode.ssContent),
713714
ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),
715+
placeholder: vnode.placeholder,
716+
714717
el: vnode.el,
715718
anchor: vnode.anchor,
716719
ctx: vnode.ctx,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy