fix(teleport): handle deferred teleport updates before and after mount (#13350)

close #13349
This commit is contained in:
edison 2025-05-20 08:27:54 +08:00 committed by GitHub
parent 163b3651d1
commit d15dce3142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 3 deletions

View File

@ -16,6 +16,7 @@ import {
render,
serialize,
serializeInner,
useModel,
withDirectives,
} from '@vue/runtime-test'
import {
@ -144,6 +145,62 @@ describe('renderer: teleport', () => {
`"<!--teleport start--><!--teleport end--><div>Footer</div><div id="targetId"><div>bar</div></div>"`,
)
})
// #13349
test('handle deferred teleport updates before and after mount', async () => {
const root = document.createElement('div')
document.body.appendChild(root)
const show = ref(false)
const data2 = ref('2')
const data3 = ref('3')
const Comp = {
props: {
modelValue: {},
modelModifiers: {},
},
emits: ['update:modelValue'],
setup(props: any) {
const data2 = useModel(props, 'modelValue')
data2.value = '2+'
return () => h('span')
},
}
createDOMApp({
setup() {
setTimeout(() => (show.value = true), 5)
setTimeout(() => (data3.value = '3+'), 10)
},
render() {
return h(Fragment, null, [
h('span', { id: 'targetId001' }),
show.value
? h(Fragment, null, [
h(Teleport, { to: '#targetId001', defer: true }, [
createTextVNode(String(data3.value)),
]),
h(Comp, {
modelValue: data2.value,
'onUpdate:modelValue': (event: any) =>
(data2.value = event),
}),
])
: createCommentVNode('v-if'),
])
},
}).mount(root)
expect(root.innerHTML).toMatchInlineSnapshot(
`"<span id="targetId001"></span><!--v-if-->"`,
)
await new Promise(r => setTimeout(r, 10))
expect(root.innerHTML).toMatchInlineSnapshot(
`"<span id="targetId001">3+</span><!--teleport start--><!--teleport end--><span></span>"`,
)
})
})
function runSharedTests(deferMode: boolean) {

View File

@ -164,15 +164,16 @@ export const TeleportImpl = {
}
if (isTeleportDeferred(n2.props)) {
n2.el!.__isMounted = false
queuePostRenderEffect(() => {
mountToTarget()
n2.el!.__isMounted = true
delete n2.el!.__isMounted
}, parentSuspense)
} else {
mountToTarget()
}
} else {
if (isTeleportDeferred(n2.props) && !n1.el!.__isMounted) {
if (isTeleportDeferred(n2.props) && n1.el!.__isMounted === false) {
queuePostRenderEffect(() => {
TeleportImpl.process(
n1,
@ -186,7 +187,6 @@ export const TeleportImpl = {
optimized,
internals,
)
delete n1.el!.__isMounted
}, parentSuspense)
return
}