@@ -245,6 +245,8 @@ export class VueElement
245
245
private _childStyles ?: Map < string , HTMLStyleElement [ ] >
246
246
private _ob ?: MutationObserver | null = null
247
247
private _slots ?: Record < string , Node [ ] >
248
+ private _slotFallbacks ?: Record < string , Node [ ] >
249
+ private _slotAnchors ?: Map < string , Node >
248
250
249
251
constructor (
250
252
/**
@@ -529,8 +531,11 @@ export class VueElement
529
531
private _createVNode ( ) : VNode < any , any > {
530
532
const baseProps : VNodeProps = { }
531
533
if ( ! this . shadowRoot ) {
532
- baseProps . onVnodeMounted = baseProps . onVnodeUpdated =
533
- this . _renderSlots . bind ( this )
534
+ baseProps . onVnodeMounted = ( ) => {
535
+ this . _captureSlotFallbacks ( )
536
+ this . _renderSlots ( )
537
+ }
538
+ baseProps . onVnodeUpdated = this . _renderSlots . bind ( this )
534
539
}
535
540
const vnode = createVNode ( this . _def , extend ( baseProps , this . _props ) )
536
541
if ( ! this . _instance ) {
@@ -617,14 +622,19 @@ export class VueElement
617
622
/**
618
623
* Only called when shadowRoot is false
619
624
*/
620
- private _parseSlots ( ) {
625
+ private _parseSlots ( remove : boolean = true ) {
621
626
const slots : VueElement [ '_slots' ] = ( this . _slots = { } )
622
- let n
623
- while ( ( n = this . firstChild ) ) {
627
+ let n = this . firstChild
628
+ while ( n ) {
624
629
const slotName =
625
630
( n . nodeType === 1 && ( n as Element ) . getAttribute ( 'slot' ) ) || 'default'
626
631
; ( slots [ slotName ] || ( slots [ slotName ] = [ ] ) ) . push ( n )
627
- this . removeChild ( n )
632
+ const next = n . nextSibling
633
+ // store the parentNode reference since node will be removed
634
+ // but it is needed during patching
635
+ ; ( n as any ) . _parentNode = n . parentNode
636
+ if ( remove ) this . removeChild ( n )
637
+ n = next
628
638
}
629
639
}
630
640
@@ -634,11 +644,18 @@ export class VueElement
634
644
private _renderSlots ( ) {
635
645
const outlets = ( this . _teleportTarget || this ) . querySelectorAll ( 'slot' )
636
646
const scopeId = this . _instance ! . type . __scopeId
647
+ this . _slotAnchors = new Map ( )
637
648
for ( let i = 0 ; i < outlets . length ; i ++ ) {
638
649
const o = outlets [ i ] as HTMLSlotElement
639
650
const slotName = o . getAttribute ( 'name' ) || 'default'
640
651
const content = this . _slots ! [ slotName ]
641
652
const parent = o . parentNode !
653
+
654
+ // insert an anchor to facilitate updates
655
+ const anchor = document . createTextNode ( '' )
656
+ this . _slotAnchors . set ( slotName , anchor )
657
+ parent . insertBefore ( anchor , o )
658
+
642
659
if ( content ) {
643
660
for ( const n of content ) {
644
661
// for :slotted css
@@ -651,23 +668,94 @@ export class VueElement
651
668
; ( child as Element ) . setAttribute ( id , '' )
652
669
}
653
670
}
654
- parent . insertBefore ( n , o )
671
+ parent . insertBefore ( n , anchor )
672
+ }
673
+ } else if ( this . _slotFallbacks ) {
674
+ const nodes = this . _slotFallbacks [ slotName ]
675
+ if ( nodes ) {
676
+ for ( const n of nodes ) {
677
+ parent . insertBefore ( n , anchor )
678
+ }
655
679
}
656
- } else {
657
- while ( o . firstChild ) parent . insertBefore ( o . firstChild , o )
658
680
}
659
681
parent . removeChild ( o )
660
682
}
661
683
}
662
684
663
685
/**
664
- * @internal
686
+ * Only called when shadowRoot is false
665
687
*/
666
- _updateSlots ( children : VNode [ ] ) : void {
667
- children . forEach ( child => {
668
- // slot children are always Fragments
669
- this . _slots ! [ child . slotName ! ] = collectFragmentElements ( child )
670
- } )
688
+ _updateSlots ( n1 : VNode , n2 : VNode ) : void {
689
+ // update v-if comment nodes in slots
690
+ const n1Children = n1 . children as VNodeArrayChildren
691
+ const n2Children = n2 . children as VNodeArrayChildren
692
+ const n1Nodes = collectElements ( n1Children )
693
+ const n2Nodes = collectElements ( n2Children )
694
+
695
+ for ( let i = 0 ; i < n1Nodes . length ; i ++ ) {
696
+ const n1Node = n1Nodes [ i ]
697
+ if ( n1Node . nodeType === 8 && ( n1Node as Comment ) . data === 'v-if' ) {
698
+ const n2Node = n2Nodes [ i ]
699
+ Object . keys ( this . _slots ! ) . forEach ( name => {
700
+ const slotNodes = this . _slots ! [ name ]
701
+ if ( slotNodes ) {
702
+ for ( const node of slotNodes ) {
703
+ if ( node === n1Node ) {
704
+ this . _slots ! [ name ] [ i ] = n2Node
705
+ continue
706
+ }
707
+ }
708
+ }
709
+ } )
710
+ }
711
+ }
712
+
713
+ // toggle between fallback and provide slot content
714
+ if ( this . _slotFallbacks ) {
715
+ const oldSlotNames = Object . keys ( this . _slots ! )
716
+ // re-parse slots
717
+ this . _parseSlots ( false )
718
+ const newSlotNames = Object . keys ( this . _slots ! )
719
+ const allSlotNames = new Set ( [ ...oldSlotNames , ...newSlotNames ] )
720
+ allSlotNames . forEach ( name => {
721
+ const fallbackNodes = this . _slotFallbacks ! [ name ]
722
+ if ( fallbackNodes ) {
723
+ // render fallback nodes for removed slots
724
+ if ( ! newSlotNames . includes ( name ) ) {
725
+ const anchor = this . _slotAnchors ! . get ( name ) !
726
+ fallbackNodes . forEach ( fallbackNode =>
727
+ this . insertBefore ( fallbackNode , anchor ) ,
728
+ )
729
+ }
730
+
731
+ // remove fallback nodes for added slots
732
+ if ( ! oldSlotNames . includes ( name ) ) {
733
+ fallbackNodes . forEach ( fallbackNode =>
734
+ this . removeChild ( fallbackNode ) ,
735
+ )
736
+ }
737
+ }
738
+ } )
739
+ }
740
+ }
741
+
742
+ /**
743
+ * Only called when shadowRoot is false
744
+ */
745
+ private _captureSlotFallbacks ( ) {
746
+ const outlets = ( this . _teleportTarget || this ) . querySelectorAll ( 'slot' )
747
+ for ( let i = 0 ; i < outlets . length ; i ++ ) {
748
+ const slotElement = outlets [ i ] as HTMLSlotElement
749
+ const slotName = slotElement . getAttribute ( 'name' ) || 'default'
750
+ const fallbackNodes : Node [ ] = [ ]
751
+ while ( slotElement . firstChild ) {
752
+ fallbackNodes . push ( slotElement . removeChild ( slotElement . firstChild ) )
753
+ }
754
+ if ( fallbackNodes . length ) {
755
+ ; ( this . _slotFallbacks || ( this . _slotFallbacks = { } ) ) [ slotName ] =
756
+ fallbackNodes
757
+ }
758
+ }
671
759
}
672
760
673
761
/**
0 commit comments