@@ -286,6 +286,7 @@ const vaporInteropImpl: Omit<
286286 unmount ( vnode , doRemove ) {
287287 const container = doRemove ? vnode . anchor ! . parentNode : undefined
288288 const instance = vnode . component as any as VaporComponentInstance
289+ let slotStartAnchor : Node | null = null
289290 if ( instance ) {
290291 // the async component may not be resolved yet, block is null
291292 if ( instance . block ) {
@@ -304,6 +305,11 @@ const vaporInteropImpl: Omit<
304305 }
305306 } else if ( vnode . vb ) {
306307 const anchor = vnode . anchor as Node | null
308+ // `hydrateSlot()` records the opening marker for VDOM SSR slot fragments
309+ // on vnode.el while vnode.anchor points at the closing marker.
310+ if ( vnode . el && vnode . el !== anchor && isComment ( vnode . el as Node , '[' ) ) {
311+ slotStartAnchor = vnode . el as Node
312+ }
307313 // Fragment child unmounts invoke VaporSlot with doRemove = false, so the
308314 // renderer does not pass us a container. Most slot blocks can still
309315 // clean themselves up without it, but KeepAlive needs the host container
@@ -317,6 +323,12 @@ const vaporInteropImpl: Omit<
317323 stopVaporSlotScope ( vnode )
318324 }
319325 if ( doRemove ) {
326+ if ( slotStartAnchor ) {
327+ const parent = slotStartAnchor . parentNode
328+ if ( parent ) {
329+ remove ( slotStartAnchor , parent )
330+ }
331+ }
320332 const anchor = vnode . anchor as Node
321333 // `container` is captured before unmount starts, but the unmount above
322334 // may already remove or move this anchor. Only remove it if it is still
@@ -362,6 +374,10 @@ const vaporInteropImpl: Omit<
362374 const selfAnchor = n1 . anchor as Node
363375 const parent = selfAnchor . parentNode as ParentNode
364376 const nextSibling = selfAnchor . nextSibling
377+ const rangeStartAnchor =
378+ n1 . el && n1 . el !== selfAnchor && isComment ( n1 . el as Node , '[' )
379+ ? ( n1 . el as Node )
380+ : undefined
365381 const oldBlockOwnsAnchor =
366382 isFragment ( n1 . vb ! ) && n1 . vb ! . anchor === selfAnchor
367383 // remove old vapor block
@@ -380,12 +396,14 @@ const vaporInteropImpl: Omit<
380396 newAnchor = selfAnchor
381397 insertAnchor = selfAnchor
382398 }
383- insert ( ( n2 . el = n2 . anchor = newAnchor ) , parent , insertAnchor )
399+ insert ( ( n2 . anchor = newAnchor ) , parent , insertAnchor )
400+ n2 . el = rangeStartAnchor || newAnchor
384401 insert ( ( n2 . vb = slotBlock ) , parent , newAnchor )
385402 } else {
386403 const vs1 = n1 . vs !
387404 const vs2 = n2 . vs !
388- n2 . el = n2 . anchor = n1 . anchor
405+ n2 . el = n1 . el
406+ n2 . anchor = n1 . anchor
389407 n2 . vb = n1 . vb
390408 ; ( vs2 . ref = vs1 . ref ) ! . value = n2 . props
391409 vs2 . scope = vs1 . scope
@@ -395,6 +413,13 @@ const vaporInteropImpl: Omit<
395413 } ,
396414
397415 move ( vnode , container , anchor , moveType ) {
416+ if (
417+ vnode . el &&
418+ vnode . el !== vnode . anchor &&
419+ isComment ( vnode . el as Node , '[' )
420+ ) {
421+ move ( vnode . el as any , container , anchor , moveType )
422+ }
398423 move ( vnode . vb || ( vnode . component as any ) , container , anchor , moveType )
399424 move ( vnode . anchor as any , container , anchor , moveType )
400425 } ,
@@ -432,11 +457,18 @@ const vaporInteropImpl: Omit<
432457 if ( ! isHydrating && ! isVdomHydrating ) return node
433458 vaporHydrateNode ( node , ( ) => {
434459 vnode . vb = renderVaporSlot ( vnode , parentComponent , parentSuspense )
435- vnode . anchor = vnode . el =
460+ const anchor =
436461 isFragment ( vnode . vb ) && vnode . vb . anchor
437462 ? vnode . vb . anchor
438463 : currentHydrationNode !
439-
464+ // VDOM SSR wraps slot output in fragment anchors. Keep that range on the
465+ // VaporSlot vnode so enabled Teleport removal can dispose both anchors.
466+ if ( isComment ( node , '[' ) && isComment ( anchor , ']' ) ) {
467+ vnode . el = node
468+ vnode . anchor = anchor
469+ } else {
470+ vnode . anchor = vnode . el = anchor
471+ }
440472 if ( __DEV__ && ! vnode . anchor ) {
441473 throw new Error (
442474 `Failed to locate slot anchor. this is likely a Vue internal bug.` ,
0 commit comments