feat(custom-element): useShadowRoot() helper

close #6113
close #8195
This commit is contained in:
Evan You 2024-08-03 14:19:19 +08:00
parent e181bff6dc
commit 5a1a89bd61
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
4 changed files with 51 additions and 3 deletions

View File

@ -417,7 +417,7 @@ export interface ComponentInternalInstance {
* is custom element?
* @internal
*/
isCE?: boolean
isCE?: Element
/**
* custom element specific HMR method
* @internal

View File

@ -11,6 +11,7 @@ import {
ref,
render,
renderSlot,
useShadowRoot,
} from '../src'
describe('defineCustomElement', () => {
@ -861,4 +862,23 @@ describe('defineCustomElement', () => {
)
})
})
describe('useCustomElementRoot', () => {
test('should work for style injection', () => {
const Foo = defineCustomElement({
setup() {
const root = useShadowRoot()!
const style = document.createElement('style')
style.innerHTML = `div { color: red; }`
root.appendChild(style)
return () => h('div', 'hello')
},
})
customElements.define('my-el', Foo)
container.innerHTML = `<my-el></my-el>`
const el = container.childNodes[0] as VueElement
const style = el.shadowRoot?.querySelector('style')!
expect(style.textContent).toBe(`div { color: red; }`)
})
})
})

View File

@ -24,6 +24,7 @@ import {
type VNodeProps,
createVNode,
defineComponent,
getCurrentInstance,
nextTick,
warn,
} from '@vue/runtime-core'
@ -191,7 +192,10 @@ export class VueElement extends BaseClass {
private _numberProps: Record<string, true> | null = null
private _styles?: HTMLStyleElement[]
private _ob?: MutationObserver | null = null
private _root: Element | ShadowRoot
/**
* @internal
*/
public _root: Element | ShadowRoot
private _slots?: Record<string, Node[]>
constructor(
@ -247,6 +251,7 @@ export class VueElement extends BaseClass {
this._ob = null
}
render(null, this._root)
this._instance!.isCE = undefined
this._instance = null
}
})
@ -395,7 +400,7 @@ export class VueElement extends BaseClass {
if (!this._instance) {
vnode.ce = instance => {
this._instance = instance
instance.isCE = true
instance.isCE = this
// HMR
if (__DEV__) {
instance.ceReload = newStyles => {
@ -508,3 +513,25 @@ export class VueElement extends BaseClass {
}
}
}
/**
* Retrieve the shadowRoot of the current custom element. Only usable in setup()
* of a `defineCustomElement` component.
*/
export function useShadowRoot(): ShadowRoot | null {
const instance = getCurrentInstance()
const el = instance && instance.isCE
if (el) {
return el.shadowRoot
} else if (__DEV__) {
if (!instance) {
warn(`useCustomElementRoot called without an active component instance.`)
} else {
warn(
`useCustomElementRoot can only be used in components defined via ` +
`defineCustomElement.`,
)
}
}
return null
}

View File

@ -242,6 +242,7 @@ function normalizeContainer(
export {
defineCustomElement,
defineSSRCustomElement,
useShadowRoot,
VueElement,
type VueElementConstructor,
} from './apiCustomElement'