Skip to content

Commit 27ba103

Browse files
committed
wip: save
1 parent 1a5ea55 commit 27ba103

File tree

2 files changed

+107
-17
lines changed

2 files changed

+107
-17
lines changed

packages/runtime-core/src/renderer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ function baseCreateRenderer(
936936
}
937937

938938
if (el._isVueCE && el._def.shadowRoot === false) {
939-
el._updateSlots(n2.children)
939+
el._updateSlots(n1, n2)
940940
}
941941
}
942942

@@ -966,7 +966,9 @@ function baseCreateRenderer(
966966
!isSameVNodeType(oldVNode, newVNode) ||
967967
// - In the case of a component, it could contain anything.
968968
oldVNode.shapeFlag & (ShapeFlags.COMPONENT | ShapeFlags.TELEPORT))
969-
? hostParentNode(oldVNode.el)!
969+
? oldVNode.el._parentNode && !oldVNode.el.isConnected
970+
? oldVNode.el._parentNode
971+
: hostParentNode(oldVNode.el)!
970972
: // In other cases, the parent container is not actually used so we
971973
// just pass the block element here to avoid a DOM parentNode call.
972974
fallbackContainer

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ export class VueElement
245245
private _childStyles?: Map<string, HTMLStyleElement[]>
246246
private _ob?: MutationObserver | null = null
247247
private _slots?: Record<string, Node[]>
248+
private _slotFallbacks?: Record<string, Node[]>
249+
private _slotAnchors?: Map<string, Node>
248250

249251
constructor(
250252
/**
@@ -529,8 +531,11 @@ export class VueElement
529531
private _createVNode(): VNode<any, any> {
530532
const baseProps: VNodeProps = {}
531533
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)
534539
}
535540
const vnode = createVNode(this._def, extend(baseProps, this._props))
536541
if (!this._instance) {
@@ -617,14 +622,19 @@ export class VueElement
617622
/**
618623
* Only called when shadowRoot is false
619624
*/
620-
private _parseSlots() {
625+
private _parseSlots(remove: boolean = true) {
621626
const slots: VueElement['_slots'] = (this._slots = {})
622-
let n
623-
while ((n = this.firstChild)) {
627+
let n = this.firstChild
628+
while (n) {
624629
const slotName =
625630
(n.nodeType === 1 && (n as Element).getAttribute('slot')) || 'default'
626631
;(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
628638
}
629639
}
630640

@@ -634,11 +644,18 @@ export class VueElement
634644
private _renderSlots() {
635645
const outlets = (this._teleportTarget || this).querySelectorAll('slot')
636646
const scopeId = this._instance!.type.__scopeId
647+
this._slotAnchors = new Map()
637648
for (let i = 0; i < outlets.length; i++) {
638649
const o = outlets[i] as HTMLSlotElement
639650
const slotName = o.getAttribute('name') || 'default'
640651
const content = this._slots![slotName]
641652
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+
642659
if (content) {
643660
for (const n of content) {
644661
// for :slotted css
@@ -651,23 +668,94 @@ export class VueElement
651668
;(child as Element).setAttribute(id, '')
652669
}
653670
}
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+
}
655679
}
656-
} else {
657-
while (o.firstChild) parent.insertBefore(o.firstChild, o)
658680
}
659681
parent.removeChild(o)
660682
}
661683
}
662684

663685
/**
664-
* @internal
686+
* Only called when shadowRoot is false
665687
*/
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+
}
671759
}
672760

673761
/**

0 commit comments

Comments
 (0)