@@ -265,7 +265,7 @@ describe('SSR hydration', () => {
265265 const fn = vi . fn ( )
266266 const teleportContainer = document . createElement ( 'div' )
267267 teleportContainer . id = 'teleport'
268- teleportContainer . innerHTML = `<span>foo</span><span class="foo"></span><!--teleport anchor-->`
268+ teleportContainer . innerHTML = `<!--teleport start anchor-->< span>foo</span><span class="foo"></span><!--teleport anchor-->`
269269 document . body . appendChild ( teleportContainer )
270270
271271 const { vnode, container } = mountWithHydration (
@@ -281,13 +281,14 @@ describe('SSR hydration', () => {
281281 expect ( vnode . anchor ) . toBe ( container . lastChild )
282282
283283 expect ( vnode . target ) . toBe ( teleportContainer )
284+ expect ( vnode . targetStart ) . toBe ( teleportContainer . childNodes [ 0 ] )
284285 expect ( ( vnode . children as VNode [ ] ) [ 0 ] . el ) . toBe (
285- teleportContainer . childNodes [ 0 ] ,
286+ teleportContainer . childNodes [ 1 ] ,
286287 )
287288 expect ( ( vnode . children as VNode [ ] ) [ 1 ] . el ) . toBe (
288- teleportContainer . childNodes [ 1 ] ,
289+ teleportContainer . childNodes [ 2 ] ,
289290 )
290- expect ( vnode . targetAnchor ) . toBe ( teleportContainer . childNodes [ 2 ] )
291+ expect ( vnode . targetAnchor ) . toBe ( teleportContainer . childNodes [ 3 ] )
291292
292293 // event handler
293294 triggerEvent ( 'click' , teleportContainer . querySelector ( '.foo' ) ! )
@@ -296,7 +297,7 @@ describe('SSR hydration', () => {
296297 msg . value = 'bar'
297298 await nextTick ( )
298299 expect ( teleportContainer . innerHTML ) . toBe (
299- `<span>bar</span><span class="bar"></span><!--teleport anchor-->` ,
300+ `<!--teleport start anchor-->< span>bar</span><span class="bar"></span><!--teleport anchor-->` ,
300301 )
301302 } )
302303
@@ -326,7 +327,7 @@ describe('SSR hydration', () => {
326327
327328 const teleportHtml = ctx . teleports ! [ '#teleport2' ]
328329 expect ( teleportHtml ) . toMatchInlineSnapshot (
329- `"<span>foo</span><span class="foo"></span><!--teleport anchor--><span>foo2</span><span class="foo2"></span><!--teleport anchor-->"` ,
330+ `"<!--teleport start anchor-->< span>foo</span><span class="foo"></span><!--teleport anchor--><!--teleport start anchor--><span>foo2</span><span class="foo2"></span><!--teleport anchor-->"` ,
330331 )
331332
332333 teleportContainer . innerHTML = teleportHtml
@@ -342,16 +343,18 @@ describe('SSR hydration', () => {
342343 expect ( teleportVnode2 . anchor ) . toBe ( container . childNodes [ 4 ] )
343344
344345 expect ( teleportVnode1 . target ) . toBe ( teleportContainer )
346+ expect ( teleportVnode1 . targetStart ) . toBe ( teleportContainer . childNodes [ 0 ] )
345347 expect ( ( teleportVnode1 as any ) . children [ 0 ] . el ) . toBe (
346- teleportContainer . childNodes [ 0 ] ,
348+ teleportContainer . childNodes [ 1 ] ,
347349 )
348- expect ( teleportVnode1 . targetAnchor ) . toBe ( teleportContainer . childNodes [ 2 ] )
350+ expect ( teleportVnode1 . targetAnchor ) . toBe ( teleportContainer . childNodes [ 3 ] )
349351
350352 expect ( teleportVnode2 . target ) . toBe ( teleportContainer )
353+ expect ( teleportVnode2 . targetStart ) . toBe ( teleportContainer . childNodes [ 4 ] )
351354 expect ( ( teleportVnode2 as any ) . children [ 0 ] . el ) . toBe (
352- teleportContainer . childNodes [ 3 ] ,
355+ teleportContainer . childNodes [ 5 ] ,
353356 )
354- expect ( teleportVnode2 . targetAnchor ) . toBe ( teleportContainer . childNodes [ 5 ] )
357+ expect ( teleportVnode2 . targetAnchor ) . toBe ( teleportContainer . childNodes [ 7 ] )
355358
356359 // // event handler
357360 triggerEvent ( 'click' , teleportContainer . querySelector ( '.foo' ) ! )
@@ -363,7 +366,7 @@ describe('SSR hydration', () => {
363366 msg . value = 'bar'
364367 await nextTick ( )
365368 expect ( teleportContainer . innerHTML ) . toMatchInlineSnapshot (
366- `"<span>bar</span><span class="bar"></span><!--teleport anchor--><span>bar2</span><span class="bar2"></span><!--teleport anchor-->"` ,
369+ `"<!--teleport start anchor-->< span>bar</span><span class="bar"></span><!--teleport anchor--><!--teleport start anchor--><span>bar2</span><span class="bar2"></span><!--teleport anchor-->"` ,
367370 )
368371 } )
369372
@@ -390,7 +393,9 @@ describe('SSR hydration', () => {
390393 )
391394
392395 const teleportHtml = ctx . teleports ! [ '#teleport3' ]
393- expect ( teleportHtml ) . toMatchInlineSnapshot ( `"<!--teleport anchor-->"` )
396+ expect ( teleportHtml ) . toMatchInlineSnapshot (
397+ `"<!--teleport start anchor--><!--teleport anchor-->"` ,
398+ )
394399
395400 teleportContainer . innerHTML = teleportHtml
396401 document . body . appendChild ( teleportContainer )
@@ -413,7 +418,8 @@ describe('SSR hydration', () => {
413418 expect ( children [ 2 ] . el ) . toBe ( container . childNodes [ 6 ] )
414419
415420 expect ( teleportVnode . target ) . toBe ( teleportContainer )
416- expect ( teleportVnode . targetAnchor ) . toBe ( teleportContainer . childNodes [ 0 ] )
421+ expect ( teleportVnode . targetStart ) . toBe ( teleportContainer . childNodes [ 0 ] )
422+ expect ( teleportVnode . targetAnchor ) . toBe ( teleportContainer . childNodes [ 1 ] )
417423
418424 // // event handler
419425 triggerEvent ( 'click' , container . querySelector ( '.foo' ) ! )
@@ -454,7 +460,7 @@ describe('SSR hydration', () => {
454460 test ( 'Teleport (as component root)' , ( ) => {
455461 const teleportContainer = document . createElement ( 'div' )
456462 teleportContainer . id = 'teleport4'
457- teleportContainer . innerHTML = `hello<!--teleport anchor-->`
463+ teleportContainer . innerHTML = `<!--teleport start anchor--> hello<!--teleport anchor-->`
458464 document . body . appendChild ( teleportContainer )
459465
460466 const wrapper = {
@@ -483,7 +489,7 @@ describe('SSR hydration', () => {
483489 test ( 'Teleport (nested)' , ( ) => {
484490 const teleportContainer = document . createElement ( 'div' )
485491 teleportContainer . id = 'teleport5'
486- teleportContainer . innerHTML = `<div><!--teleport start--><!--teleport end--></div><!--teleport anchor--><div>child</div><!--teleport anchor-->`
492+ teleportContainer . innerHTML = `<!--teleport start anchor-->< div><!--teleport start--><!--teleport end--></div><!--teleport anchor--><!--teleport start anchor--><div>child</div><!--teleport anchor-->`
487493 document . body . appendChild ( teleportContainer )
488494
489495 const { vnode, container } = mountWithHydration (
@@ -498,7 +504,7 @@ describe('SSR hydration', () => {
498504 expect ( vnode . anchor ) . toBe ( container . lastChild )
499505
500506 const childDivVNode = ( vnode as any ) . children [ 0 ]
501- const div = teleportContainer . firstChild
507+ const div = teleportContainer . childNodes [ 1 ]
502508 expect ( childDivVNode . el ) . toBe ( div )
503509 expect ( vnode . targetAnchor ) . toBe ( div ?. nextSibling )
504510
@@ -548,6 +554,66 @@ describe('SSR hydration', () => {
548554 teleportContainer . id = 'target'
549555 document . body . appendChild ( teleportContainer )
550556
557+ // server render
558+ const ctx : SSRContext = { }
559+ container . innerHTML = await renderToString ( h ( App ) , ctx )
560+ expect ( container . innerHTML ) . toBe (
561+ '<div><!--teleport start--><!--teleport end--></div>' ,
562+ )
563+ teleportContainer . innerHTML = ctx . teleports ! [ '#target' ]
564+
565+ // hydrate
566+ createSSRApp ( App ) . mount ( container )
567+ expect ( container . innerHTML ) . toBe (
568+ '<div><!--teleport start--><!--teleport end--></div>' ,
569+ )
570+ expect ( teleportContainer . innerHTML ) . toBe (
571+ '<!--teleport start anchor--><span>Teleported Comp1</span><!--teleport anchor-->' ,
572+ )
573+ expect ( `Hydration children mismatch` ) . not . toHaveBeenWarned ( )
574+
575+ toggle . value = false
576+ await nextTick ( )
577+ expect ( container . innerHTML ) . toBe ( '<div><div>Comp2</div></div>' )
578+ expect ( teleportContainer . innerHTML ) . toBe ( '' )
579+ } )
580+
581+ test ( 'Teleport unmount (mismatch + full integration)' , async ( ) => {
582+ const Comp1 = {
583+ template : `
584+ <Teleport to="#target">
585+ <span>Teleported Comp1</span>
586+ </Teleport>
587+ ` ,
588+ }
589+ const Comp2 = {
590+ template : `
591+ <div>Comp2</div>
592+ ` ,
593+ }
594+
595+ const toggle = ref ( true )
596+ const App = {
597+ template : `
598+ <div>
599+ <Comp1 v-if="toggle"/>
600+ <Comp2 v-else/>
601+ </div>
602+ ` ,
603+ components : {
604+ Comp1,
605+ Comp2,
606+ } ,
607+ setup ( ) {
608+ return { toggle }
609+ } ,
610+ }
611+
612+ const container = document . createElement ( 'div' )
613+ const teleportContainer = document . createElement ( 'div' )
614+ teleportContainer . id = 'target'
615+ document . body . appendChild ( teleportContainer )
616+
551617 // server render
552618 container . innerHTML = await renderToString ( h ( App ) )
553619 expect ( container . innerHTML ) . toBe (
@@ -569,7 +635,7 @@ describe('SSR hydration', () => {
569635 expect ( teleportContainer . innerHTML ) . toBe ( '' )
570636 } )
571637
572- test ( 'Teleport target change (full integration)' , async ( ) => {
638+ test ( 'Teleport target change (mismatch + full integration)' , async ( ) => {
573639 const target = ref ( '#target1' )
574640 const Comp = {
575641 template : `
0 commit comments