diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
index efb33e64d..bc827dd73 100644
--- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
@@ -151,7 +151,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
`;
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
-"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, insert as _insert, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, insert as _insert, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("
{{ bar }}
")
const t1 = _template("")
@@ -162,7 +162,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const n1 = _createComponent(_component_Comp)
const n2 = _createTextNode(() => [_ctx.bar])
_insert([n1, n2], n3)
- _renderEffect(() => _setDynamicProp(n3, "id", _ctx.foo))
+ _renderEffect(() => _setDOMProp(n3, "id", _ctx.foo))
return [n0, n3]
}"
`;
@@ -177,7 +177,7 @@ export function render(_ctx) {
`;
exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
+"import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, setDOMProp as _setDOMProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
const t0 = _template("")
_delegateEvents("click")
@@ -186,7 +186,7 @@ export function render(_ctx) {
_delegate(n0, "click", () => _ctx.handleClick)
_setInheritAttrs(["id"])
_renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
- _renderEffect(() => _setDynamicProp(n0, "id", _ctx.count, true))
+ _renderEffect(() => _setDOMProp(n0, "id", _ctx.count, true))
return n0
}"
`;
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
index 16e60bd84..822a6af66 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
@@ -121,14 +121,100 @@ export function render(_ctx) {
}"
`;
+exports[`compiler v-bind > HTML global attributes should set as dom prop 1`] = `
+"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+const t0 = _template("")
+
+export function render(_ctx) {
+ const n0 = t0()
+ _setInheritAttrs(["id", "title", "lang", "dir", "tabindex"])
+ _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
+ _renderEffect(() => _setDOMProp(n0, "title", _ctx.title, true))
+ _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
+ _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
+ _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > MathML global attributes should set as dom prop 1`] = `
+"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+const t0 = _template("")
+
+export function render(_ctx) {
+ const n0 = t0()
+ _setInheritAttrs(["autofucus", "dir", "displaystyle", "mathcolor", "tabindex"])
+ _renderEffect(() => _setDOMProp(n0, "autofucus", _ctx.autofucus, true))
+ _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
+ _renderEffect(() => _setDOMProp(n0, "displaystyle", _ctx.displaystyle, true))
+ _renderEffect(() => _setDOMProp(n0, "mathcolor", _ctx.mathcolor, true))
+ _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > SVG global attributes should set as dom prop 1`] = `
+"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+const t0 = _template("")
+
+export function render(_ctx) {
+ const n0 = t0()
+ _setInheritAttrs(["id", "lang", "tabindex"])
+ _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
+ _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
+ _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > attributes must be set as attribute 1`] = `
+"import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+const t0 = _template("")
+const t1 = _template("")
+const t2 = _template("")
+const t3 = _template("")
+const t4 = _template("")
+const t5 = _template("")
+const t6 = _template("")
+
+export function render(_ctx) {
+ const n0 = t0()
+ const n1 = t1()
+ const n2 = t2()
+ const n3 = t3()
+ const n4 = t4()
+ const n5 = t5()
+ const n6 = t6()
+ _renderEffect(() => _setAttr(n0, "spellcheck", _ctx.spellcheck))
+ _renderEffect(() => _setAttr(n0, "draggable", _ctx.draggable))
+ _renderEffect(() => _setAttr(n0, "translate", _ctx.translate))
+ _renderEffect(() => _setAttr(n0, "form", _ctx.form))
+ _renderEffect(() => _setAttr(n1, "list", _ctx.list))
+ _renderEffect(() => _setAttr(n2, "type", _ctx.type))
+ _renderEffect(() => {
+ _setAttr(n3, "width", _ctx.width)
+ _setAttr(n4, "width", _ctx.width)
+ _setAttr(n5, "width", _ctx.width)
+ _setAttr(n6, "width", _ctx.width)
+ })
+ _renderEffect(() => {
+ _setAttr(n3, "height", _ctx.height)
+ _setAttr(n4, "height", _ctx.height)
+ _setAttr(n5, "height", _ctx.height)
+ _setAttr(n6, "height", _ctx.height)
+ })
+ return [n0, n1, n2, n3, n4, n5, n6]
+}"
+`;
+
exports[`compiler v-bind > basic 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id"])
- _renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
+ _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
return n0
}"
`;
@@ -170,13 +256,13 @@ export function render(_ctx) {
`;
exports[`compiler v-bind > no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id"])
- _renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
+ _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
return n0
}"
`;
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
index 213879fea..2665b6157 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
@@ -1,12 +1,12 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-once > as root node 1`] = `
-"import { setDynamicProp as _setDynamicProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
+"import { setDOMProp as _setDOMProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("")
export function render(_ctx) {
const n0 = t0()
- _setDynamicProp(n0, "id", _ctx.foo, true)
+ _setDOMProp(n0, "id", _ctx.foo, true)
_setInheritAttrs(["id"])
return n0
}"
@@ -52,13 +52,13 @@ export function render(_ctx) {
`;
exports[`compiler: v-once > on nested plain element 1`] = `
-"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("
")
export function render(_ctx) {
const n1 = t0()
const n0 = n1.firstChild
- _setDynamicProp(n0, "id", _ctx.foo)
+ _setDOMProp(n0, "id", _ctx.foo)
return n1
}"
`;
diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
index a419f55dd..7f14bb25d 100644
--- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
@@ -74,7 +74,7 @@ describe('compiler v-bind', () => {
})
expect(code).matchSnapshot()
- expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
+ expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
})
test('no expression', () => {
@@ -104,7 +104,7 @@ describe('compiler v-bind', () => {
],
},
})
- expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
+ expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
})
test('no expression (shorthand)', () => {
@@ -527,6 +527,73 @@ describe('compiler v-bind', () => {
expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar, true)')
})
+ test('attributes must be set as attribute', () => {
+ const { code } = compileWithVBind(`
+
+
+
+
+
+
+
+ `)
+
+ expect(code).matchSnapshot()
+ expect(code).contains('_setAttr(n0, "spellcheck", _ctx.spellcheck)')
+ expect(code).contains('_setAttr(n0, "draggable", _ctx.draggable)')
+ expect(code).contains('_setAttr(n0, "translate", _ctx.translate)')
+ expect(code).contains('_setAttr(n0, "form", _ctx.form)')
+ expect(code).contains('_setAttr(n1, "list", _ctx.list)')
+ expect(code).contains('_setAttr(n2, "type", _ctx.type)')
+ expect(code).contains('_setAttr(n3, "width", _ctx.width)')
+ expect(code).contains('_setAttr(n3, "height", _ctx.height)')
+ expect(code).contains('_setAttr(n4, "width", _ctx.width)')
+ expect(code).contains('_setAttr(n4, "height", _ctx.height)')
+ expect(code).contains('_setAttr(n5, "width", _ctx.width)')
+ expect(code).contains('_setAttr(n5, "height", _ctx.height)')
+ expect(code).contains('_setAttr(n6, "width", _ctx.width)')
+ expect(code).contains('_setAttr(n6, "height", _ctx.height)')
+ })
+
+ test('HTML global attributes should set as dom prop', () => {
+ const { code } = compileWithVBind(`
+
+ `)
+
+ expect(code).matchSnapshot()
+ expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
+ expect(code).contains('_setDOMProp(n0, "title", _ctx.title, true)')
+ expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
+ expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
+ expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+ })
+
+ test('SVG global attributes should set as dom prop', () => {
+ const { code } = compileWithVBind(`
+
+ `)
+
+ expect(code).matchSnapshot()
+ expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
+ expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
+ expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+ })
+
+ test('MathML global attributes should set as dom prop', () => {
+ const { code } = compileWithVBind(`
+
+ `)
+
+ expect(code).matchSnapshot()
+ expect(code).contains('_setDOMProp(n0, "autofucus", _ctx.autofucus, true)')
+ expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
+ expect(code).contains(
+ '_setDOMProp(n0, "displaystyle", _ctx.displaystyle, true)',
+ )
+ expect(code).contains('_setDOMProp(n0, "mathcolor", _ctx.mathcolor, true)')
+ expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+ })
+
test('number value', () => {
const { code } = compileWithVBind(``)
expect(code).matchSnapshot()
diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts
index 120aa3733..295b177d6 100644
--- a/packages/compiler-vapor/src/generators/prop.ts
+++ b/packages/compiler-vapor/src/generators/prop.ts
@@ -21,7 +21,17 @@ import {
genCall,
genMulti,
} from './utils'
-import { toHandlerKey } from '@vue/shared'
+import {
+ attributeCache,
+ isHTMLGlobalAttr,
+ isHTMLTag,
+ isMathMLGlobalAttr,
+ isMathMLTag,
+ isSVGTag,
+ isSvgGlobalAttr,
+ shouldSetAsAttr,
+ toHandlerKey,
+} from '@vue/shared'
// only the static key prop will reach here
export function genSetProp(
@@ -31,9 +41,12 @@ export function genSetProp(
const { vaporHelper } = context
const {
prop: { key, values, modifier },
+ tag,
} = oper
const keyName = key.content
+ const tagName = tag.toUpperCase()
+ const attrCacheKey = `${tagName}_${keyName}`
let helperName: VaporHelper
let omitKey = false
@@ -45,6 +58,21 @@ export function genSetProp(
omitKey = true
} else if (modifier) {
helperName = modifier === '.' ? 'setDOMProp' : 'setAttr'
+ } else if (
+ attributeCache[attrCacheKey] === undefined
+ ? (attributeCache[attrCacheKey] = shouldSetAsAttr(
+ tag.toUpperCase(),
+ keyName,
+ ))
+ : attributeCache[attrCacheKey]
+ ) {
+ helperName = 'setAttr'
+ } else if (
+ (isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
+ (isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
+ (isMathMLTag(tag) && isMathMLGlobalAttr(keyName))
+ ) {
+ helperName = 'setDOMProp'
} else {
helperName = 'setDynamicProp'
}
diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts
index 9cca9bf3c..7d1ddac89 100644
--- a/packages/compiler-vapor/src/ir/index.ts
+++ b/packages/compiler-vapor/src/ir/index.ts
@@ -94,6 +94,7 @@ export interface SetPropIRNode extends BaseIRNode {
element: number
prop: IRProp
root: boolean
+ tag: string
}
export interface SetDynamicPropsIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts
index 3356a1d77..51f0e427e 100644
--- a/packages/compiler-vapor/src/transforms/transformElement.ts
+++ b/packages/compiler-vapor/src/transforms/transformElement.ts
@@ -210,6 +210,7 @@ function transformNativeElement(
element: context.reference(),
prop,
root: singleRoot,
+ tag,
})
}
}
diff --git a/packages/runtime-vapor/src/dom/prop.ts b/packages/runtime-vapor/src/dom/prop.ts
index 48d95173e..8dfe24875 100644
--- a/packages/runtime-vapor/src/dom/prop.ts
+++ b/packages/runtime-vapor/src/dom/prop.ts
@@ -1,4 +1,5 @@
import {
+ attributeCache,
includeBooleanAttr,
isArray,
isFunction,
@@ -7,6 +8,7 @@ import {
isString,
normalizeClass,
normalizeStyle,
+ shouldSetAsAttr,
toDisplayString,
} from '@vue/shared'
import { warn } from '../warning'
@@ -242,45 +244,15 @@ function shouldSetAsProp(
return false
}
- // these are enumerated attrs, however their corresponding DOM properties
- // are actually booleans - this leads to setting it with a string "false"
- // value leading it to be coerced to `true`, so we need to always treat
- // them as attributes.
- // Note that `contentEditable` doesn't have this problem: its DOM
- // property is also enumerated string values.
- if (key === 'spellcheck' || key === 'draggable' || key === 'translate') {
+ const attrCacheKey = `${el.tagName}_${key}`
+ if (
+ attributeCache[attrCacheKey] === undefined
+ ? (attributeCache[attrCacheKey] = shouldSetAsAttr(el.tagName, key))
+ : attributeCache[attrCacheKey]
+ ) {
return false
}
- // #1787, #2840 form property on form elements is readonly and must be set as
- // attribute.
- if (key === 'form') {
- return false
- }
-
- // #1526 must be set as attribute
- if (key === 'list' && el.tagName === 'INPUT') {
- return false
- }
-
- // #2766