fix(custom-element): ensure exposed methods are accessible from custom elements by making them enumerable (#13634)

close #13632
This commit is contained in:
linzhe 2025-07-23 08:40:40 +08:00 committed by GitHub
parent c5f7db1154
commit 90573b06bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 0 deletions

View File

@ -756,6 +756,7 @@ export function applyOptions(instance: ComponentInternalInstance): void {
Object.defineProperty(exposed, key, { Object.defineProperty(exposed, key, {
get: () => publicThis[key], get: () => publicThis[key],
set: val => (publicThis[key] = val), set: val => (publicThis[key] = val),
enumerable: true,
}) })
}) })
} else if (!instance.exposed) { } else if (!instance.exposed) {

View File

@ -1402,6 +1402,34 @@ describe('defineCustomElement', () => {
}) })
describe('expose', () => { describe('expose', () => {
test('expose w/ options api', async () => {
const E = defineCustomElement({
data() {
return {
value: 0,
}
},
methods: {
foo() {
;(this as any).value++
},
},
expose: ['foo'],
render(_ctx: any) {
return h('div', null, _ctx.value)
},
})
customElements.define('my-el-expose-options-api', E)
container.innerHTML = `<my-el-expose-options-api></my-el-expose-options-api>`
const e = container.childNodes[0] as VueElement & {
foo: () => void
}
expect(e.shadowRoot!.innerHTML).toBe(`<div>0</div>`)
e.foo()
await nextTick()
expect(e.shadowRoot!.innerHTML).toBe(`<div>1</div>`)
})
test('expose attributes and callback', async () => { test('expose attributes and callback', async () => {
type SetValue = (value: string) => void type SetValue = (value: string) => void
let fn: MockedFunction<SetValue> let fn: MockedFunction<SetValue>