diff --git a/packages/runtime-core/__tests__/components/Teleport.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts index 4c35b1f2d..69a1c4cb2 100644 --- a/packages/runtime-core/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -16,6 +16,7 @@ import { render, serialize, serializeInner, + useModel, withDirectives, } from '@vue/runtime-test' import { @@ -144,6 +145,62 @@ describe('renderer: teleport', () => { `"
Footer
bar
"`, ) }) + + // #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( + `""`, + ) + + await new Promise(r => setTimeout(r, 10)) + expect(root.innerHTML).toMatchInlineSnapshot( + `"3+"`, + ) + }) }) function runSharedTests(deferMode: boolean) { diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index fc2ee4c08..c37356a78 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -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 }