diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts
index 6b9f7d1391e..df438d47eee 100644
--- a/packages/runtime-dom/__tests__/customElement.spec.ts
+++ b/packages/runtime-dom/__tests__/customElement.spec.ts
@@ -396,6 +396,38 @@ describe('defineCustomElement', () => {
expect(e.value).toBe('hi')
})
+ // #12214
+ test('Boolean prop with default true', async () => {
+ const E = defineCustomElement({
+ props: {
+ foo: {
+ type: Boolean,
+ default: true,
+ },
+ },
+ render() {
+ return String(this.foo)
+ },
+ })
+ customElements.define('my-el-default-true', E)
+ container.innerHTML = ``
+ const e = container.childNodes[0] as HTMLElement & { foo: any },
+ shadowRoot = e.shadowRoot as ShadowRoot
+ expect(shadowRoot.innerHTML).toBe('true')
+ e.foo = undefined
+ await nextTick()
+ expect(shadowRoot.innerHTML).toBe('true')
+ e.foo = false
+ await nextTick()
+ expect(shadowRoot.innerHTML).toBe('false')
+ e.foo = null
+ await nextTick()
+ expect(shadowRoot.innerHTML).toBe('null')
+ e.foo = ''
+ await nextTick()
+ expect(shadowRoot.innerHTML).toBe('true')
+ })
+
test('support direct setup function syntax with extra options', () => {
const E = defineCustomElement(
props => {
diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts
index 6ddaf897130..c90c5725e17 100644
--- a/packages/runtime-dom/src/apiCustomElement.ts
+++ b/packages/runtime-dom/src/apiCustomElement.ts
@@ -232,6 +232,8 @@ export class VueElement
private _styleChildren = new WeakSet()
private _pendingResolve: Promise | undefined
private _parent: VueElement | undefined
+ private _removedAttributes = new Set()
+
/**
* dev only
*/
@@ -466,8 +468,12 @@ export class VueElement
protected _setAttr(key: string): void {
if (key.startsWith('data-v-')) return
const has = this.hasAttribute(key)
- let value = has ? this.getAttribute(key) : REMOVAL
const camelKey = camelize(key)
+ if (this._removedAttributes.has(camelKey)) {
+ if (!has) return
+ else this._removedAttributes.delete(camelKey)
+ }
+ let value = has ? this.getAttribute(key) : REMOVAL
if (has && this._numberProps && this._numberProps[camelKey]) {
value = toNumber(value)
}
@@ -511,6 +517,7 @@ export class VueElement
this.setAttribute(hyphenate(key), val + '')
} else if (!val) {
this.removeAttribute(hyphenate(key))
+ this._removedAttributes.add(key)
}
}
}