diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 907b299b824..12e67822697 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -886,6 +886,39 @@ describe('defineCustomElement', () => { assertStyles(el, [`div { color: blue; }`, `div { color: red; }`]) }) + test('child components styles should before parent styles', async () => { + const Baz = () => h(Bar) + const Bar = defineComponent({ + styles: [`div { color: green; }`], + render() { + return 'bar' + }, + }) + const WrapperBar = defineComponent({ + styles: [`div { color: blue; }`], + render() { + return h(Baz) + }, + }) + const WBaz = () => h(WrapperBar) + const Foo = defineCustomElement({ + styles: [`div { color: red; }`], + render() { + return [h(Baz), h(WBaz)] + }, + }) + customElements.define('my-el-with-wrapper-child-styles', Foo) + container.innerHTML = `` + const el = container.childNodes[0] as VueElement + + // inject order should be child -> parent + assertStyles(el, [ + `div { color: green; }`, + `div { color: blue; }`, + `div { color: red; }`, + ]) + }) + test('with nonce', () => { const Foo = defineCustomElement( { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index f0724663090..6ee67745810 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -229,7 +229,7 @@ export class VueElement private _connected = false private _resolved = false private _numberProps: Record | null = null - private _styleChildren = new WeakSet() + private _styleChildren = new WeakMap() private _pendingResolve: Promise | undefined private _parent: VueElement | undefined /** @@ -596,18 +596,25 @@ export class VueElement owner?: ConcreteComponent, ) { if (!styles) return + const styleElements: HTMLStyleElement[] = [] if (owner) { - if (owner === this._def || this._styleChildren.has(owner)) { + if (owner === this._def) return + const existingStyles = this._styleChildren.get(owner) + if (existingStyles) { + // move existing styles to the top + this.shadowRoot!.prepend(...existingStyles) return } - this._styleChildren.add(owner) + this._styleChildren.set(owner, styleElements) } + const nonce = this._nonce for (let i = styles.length - 1; i >= 0; i--) { const s = document.createElement('style') if (nonce) s.setAttribute('nonce', nonce) s.textContent = styles[i] this.shadowRoot!.prepend(s) + if (owner) styleElements.unshift(s) // record for HMR if (__DEV__) { if (owner) {