From 836db6bce6bfdf0f704f26e26b703aac253d12af Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 28 Oct 2025 17:26:56 +0800 Subject: [PATCH] wip: save --- packages/runtime-dom/src/apiCustomElement.ts | 63 ++++++++++++++++++- .../__tests__/customElement.spec.ts | 6 +- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 341ed48e3d..1a67726de0 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -632,11 +632,15 @@ export abstract class VueElementBase< protected _renderSlots(): void { const outlets = this._getSlots() const scopeId = this._instance!.type.__scopeId + const slotReplacements: Map = new Map() + for (let i = 0; i < outlets.length; i++) { const o = outlets[i] as HTMLSlotElement const slotName = o.getAttribute('name') || 'default' const content = this._slots![slotName] const parent = o.parentNode! + const replacementNodes: Node[] = [] + if (content) { for (const n of content) { // for :slotted css @@ -650,11 +654,66 @@ export abstract class VueElementBase< } } parent.insertBefore(n, o) + replacementNodes.push(n) + } + } else { + while (o.firstChild) { + const child = o.firstChild + parent.insertBefore(child, o) + replacementNodes.push(child) + } + } + + slotReplacements.set(o, replacementNodes) + } + + // For Vapor: update fragment nodes before removing slots from DOM + if (slotReplacements.size > 0 && this._instance && this._instance.vapor) { + // @ts-expect-error TODO refactor + this._replaceNodesInFragments(this._instance.block, slotReplacements) + } + + // Now safe to remove slots from DOM + slotReplacements.forEach((_, o) => o.parentNode!.removeChild(o)) + } + + /** + * Replace slot nodes with their content in fragment nodes arrays + * @internal + */ + private _replaceNodesInFragments( + block: any, + replacements: Map, + ): void { + if (!block) return + + if (Array.isArray(block)) { + for (let i = 0; i < block.length; i++) { + this._replaceNodesInFragments(block[i], replacements) + } + } else if (block.nodes !== undefined) { + // This is a fragment with nodes property + if (Array.isArray(block.nodes)) { + // Replace slot nodes with their content + const newNodes: any[] = [] + for (const node of block.nodes) { + if (node instanceof Node && replacements.has(node)) { + // Replace with the content nodes + newNodes.push(...replacements.get(node)!) + } else { + newNodes.push(node) + // Recursively process nested fragments + this._replaceNodesInFragments(node, replacements) + } } + block.nodes = newNodes + } else if (block.nodes instanceof Node && replacements.has(block.nodes)) { + // Replace single node with its content + const replacement = replacements.get(block.nodes)! + block.nodes = replacement.length === 1 ? replacement[0] : replacement } else { - while (o.firstChild) parent.insertBefore(o.firstChild, o) + this._replaceNodesInFragments(block.nodes, replacements) } - parent.removeChild(o) } } diff --git a/packages/runtime-vapor/__tests__/customElement.spec.ts b/packages/runtime-vapor/__tests__/customElement.spec.ts index 894d497639..3f0f35fa36 100644 --- a/packages/runtime-vapor/__tests__/customElement.spec.ts +++ b/packages/runtime-vapor/__tests__/customElement.spec.ts @@ -1344,7 +1344,7 @@ describe('defineVaporCustomElement', () => { ) customElements.define('my-el-shadowroot-false-slots', ES) - test.todo('should render slots', async () => { + test('should render slots', async () => { container.innerHTML = `` + `defaulttext` + @@ -1362,7 +1362,9 @@ describe('defineVaporCustomElement', () => { toggle.value = false await nextTick() expect(e.innerHTML).toBe( - `defaulttext` + `` + `
fallback
`, + `defaulttext` + + `` + + `
fallback
`, ) }) -- 2.47.3