mirror of https://github.com/vuejs/core.git
feat(custom-element): support for expose on customElement (#6256)
close #5540
This commit is contained in:
parent
5a1a89bd61
commit
af838c1b5e
|
@ -1,3 +1,4 @@
|
||||||
|
import type { MockedFunction } from 'vitest'
|
||||||
import {
|
import {
|
||||||
type Ref,
|
type Ref,
|
||||||
type VueElement,
|
type VueElement,
|
||||||
|
@ -881,4 +882,64 @@ describe('defineCustomElement', () => {
|
||||||
expect(style.textContent).toBe(`div { color: red; }`)
|
expect(style.textContent).toBe(`div { color: red; }`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('expose', () => {
|
||||||
|
test('expose attributes and callback', async () => {
|
||||||
|
type SetValue = (value: string) => void
|
||||||
|
let fn: MockedFunction<SetValue>
|
||||||
|
|
||||||
|
const E = defineCustomElement({
|
||||||
|
setup(_, { expose }) {
|
||||||
|
const value = ref('hello')
|
||||||
|
|
||||||
|
const setValue = (fn = vi.fn((_value: string) => {
|
||||||
|
value.value = _value
|
||||||
|
}))
|
||||||
|
|
||||||
|
expose({
|
||||||
|
setValue,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => h('div', null, [value.value])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
customElements.define('my-el-expose', E)
|
||||||
|
|
||||||
|
container.innerHTML = `<my-el-expose></my-el-expose>`
|
||||||
|
const e = container.childNodes[0] as VueElement & {
|
||||||
|
value: string
|
||||||
|
setValue: MockedFunction<SetValue>
|
||||||
|
}
|
||||||
|
expect(e.shadowRoot!.innerHTML).toBe(`<div>hello</div>`)
|
||||||
|
expect(e.value).toBe('hello')
|
||||||
|
expect(e.setValue).toBe(fn!)
|
||||||
|
e.setValue('world')
|
||||||
|
expect(e.value).toBe('world')
|
||||||
|
await nextTick()
|
||||||
|
expect(e.shadowRoot!.innerHTML).toBe(`<div>world</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warning when exposing an existing property', () => {
|
||||||
|
const E = defineCustomElement({
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
setup(props, { expose }) {
|
||||||
|
expose({
|
||||||
|
value: 'hello',
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => h('div', null, [props.value])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
customElements.define('my-el-expose-two', E)
|
||||||
|
|
||||||
|
container.innerHTML = `<my-el-expose-two value="world"></my-el-expose-two>`
|
||||||
|
|
||||||
|
expect(
|
||||||
|
`[Vue warn]: Exposed property "value" already exists on custom element.`,
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,11 +26,13 @@ import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
nextTick,
|
nextTick,
|
||||||
|
unref,
|
||||||
warn,
|
warn,
|
||||||
} from '@vue/runtime-core'
|
} from '@vue/runtime-core'
|
||||||
import {
|
import {
|
||||||
camelize,
|
camelize,
|
||||||
extend,
|
extend,
|
||||||
|
hasOwn,
|
||||||
hyphenate,
|
hyphenate,
|
||||||
isArray,
|
isArray,
|
||||||
isPlainObject,
|
isPlainObject,
|
||||||
|
@ -308,6 +310,9 @@ export class VueElement extends BaseClass {
|
||||||
|
|
||||||
// initial render
|
// initial render
|
||||||
this._update()
|
this._update()
|
||||||
|
|
||||||
|
// apply expose
|
||||||
|
this._applyExpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const asyncDef = (this._def as ComponentOptions).__asyncLoader
|
const asyncDef = (this._def as ComponentOptions).__asyncLoader
|
||||||
|
@ -342,6 +347,22 @@ export class VueElement extends BaseClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _applyExpose() {
|
||||||
|
const exposed = this._instance && this._instance.exposed
|
||||||
|
if (!exposed) return
|
||||||
|
for (const key in exposed) {
|
||||||
|
if (!hasOwn(this, key)) {
|
||||||
|
// exposed properties are readonly
|
||||||
|
Object.defineProperty(this, key, {
|
||||||
|
// unwrap ref to be consistent with public instance behavior
|
||||||
|
get: () => unref(exposed[key]),
|
||||||
|
})
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn(`Exposed property "${key}" already exists on custom element.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected _setAttr(key: string) {
|
protected _setAttr(key: string) {
|
||||||
if (key.startsWith('data-v-')) return
|
if (key.startsWith('data-v-')) return
|
||||||
let value = this.hasAttribute(key) ? this.getAttribute(key) : undefined
|
let value = this.hasAttribute(key) ? this.getAttribute(key) : undefined
|
||||||
|
|
Loading…
Reference in New Issue