diff --git a/README.md b/README.md
index 9a546d189..8ff103f92 100644
--- a/README.md
+++ b/README.md
@@ -29,10 +29,10 @@ PR are welcome!
- [x] `v-bind`
- [x] simple expression
- [x] compound expression
- - [ ] modifiers
+ - [x] modifiers
- [x] .camel
- - [ ] .prop
- - [ ] .attr
+ - [x] .prop
+ - [x] .attr
- [x] `v-on`
- [x] simple expression
- [x] compound expression
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 0f5e5203c..1484e4ea7 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
@@ -1,28 +1,28 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler v-bind > .attr modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("
")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)
+ _setAttr(n1, "foo-bar", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)
+ _setAttr(n1, "foo-bar", undefined, _ctx.fooBar)
})
return n0
}"
@@ -71,42 +71,42 @@ export function render(_ctx) {
`;
exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.id)
})
return n0
}"
@@ -127,14 +127,14 @@ export function render(_ctx) {
`;
exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
})
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 7a761ed3c..4fb9d979d 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
@@ -13,7 +13,7 @@ export function render(_ctx) {
`;
exports[`compiler: v-once > basic 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("
")
@@ -21,7 +21,7 @@ export function render(_ctx) {
const { 0: [n3, { 1: [n2],}],} = _children(n0)
const n1 = _createTextNode(_ctx.msg)
_setText(n1, undefined, _ctx.msg)
- _setDynamicProp(n2, "class", undefined, _ctx.clz)
+ _setClass(n2, "class", undefined, _ctx.clz)
_prepend(n3, n1)
return n0
}"
diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
index de35a2409..b8f5d335d 100644
--- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
@@ -189,6 +189,8 @@ describe('compiler v-bind', () => {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: undefined,
})
expect(code).matchSnapshot()
@@ -207,6 +209,8 @@ describe('compiler v-bind', () => {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: undefined,
})
expect(code).matchSnapshot()
@@ -220,7 +224,6 @@ describe('compiler v-bind', () => {
const { ir, code } = compileWithVBind(``)
expect(ir.effect[0].operations[0]).toMatchObject({
- runtimeCamelize: true,
key: {
content: `foo`,
isStatic: false,
@@ -229,6 +232,8 @@ describe('compiler v-bind', () => {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: true,
+ modifier: undefined,
})
expect(code).matchSnapshot()
@@ -245,18 +250,20 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
})
test('.prop modifier w/ no expression', () => {
@@ -264,20 +271,20 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
})
test('.prop modifier w/ dynamic arg', () => {
@@ -292,6 +299,8 @@ describe('compiler v-bind', () => {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
@@ -308,18 +317,20 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
})
test('.prop modifier (shortband) w/ no expression', () => {
@@ -327,20 +338,20 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
})
test('.attr modifier', () => {
@@ -348,18 +359,20 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `^foo-bar`,
+ content: `foo-bar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '^',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)')
+ expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.id)')
})
test('.attr modifier w/ no expression', () => {
@@ -367,19 +380,19 @@ describe('compiler v-bind', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `^foo-bar`,
+ content: `foo-bar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '^',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.fooBar)')
})
})
diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts
index 89e0fc6df..4dd4a1c95 100644
--- a/packages/compiler-vapor/src/generators/prop.ts
+++ b/packages/compiler-vapor/src/generators/prop.ts
@@ -1,20 +1,56 @@
import type { CodegenContext } from '../generate'
import type { SetPropIRNode } from '../ir'
import { genExpression } from './expression'
+import { isString } from '@vue/shared'
export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
newline()
+
+ const element = `n${oper.element}`
+
+ // fast path for static props
+ if (isString(oper.key) || oper.key.isStatic) {
+ const keyName = isString(oper.key) ? oper.key : oper.key.content
+
+ let helperName: string | undefined
+ if (keyName === 'class') {
+ helperName = 'setClass'
+ } else if (keyName === 'style') {
+ helperName = 'setStyle'
+ } else if (oper.modifier) {
+ helperName = oper.modifier === '.' ? 'setDOMProp' : 'setAttr'
+ }
+
+ if (helperName) {
+ pushFnCall(
+ vaporHelper(helperName),
+ element,
+ () => {
+ const expr = () => genExpression(oper.key, context)
+ if (oper.runtimeCamelize) {
+ pushFnCall(helper('camelize'), expr)
+ } else {
+ expr()
+ }
+ },
+ 'undefined',
+ () => genExpression(oper.value, context),
+ )
+ return
+ }
+ }
+
pushFnCall(
vaporHelper('setDynamicProp'),
- `n${oper.element}`,
+ element,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
- } else if (oper.runtimePrefix) {
- pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () =>
+ } else if (oper.modifier) {
+ pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
genExpression(oper.key, context),
)
} else {
diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts
index 00fcdb587..1c2f6ee30 100644
--- a/packages/compiler-vapor/src/ir.ts
+++ b/packages/compiler-vapor/src/ir.ts
@@ -63,8 +63,8 @@ export interface SetPropIRNode extends BaseIRNode {
element: number
key: IRExpression
value: IRExpression
+ modifier?: '.' | '^'
runtimeCamelize: boolean
- runtimePrefix?: string
}
export interface SetTextIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transforms/vBind.ts b/packages/compiler-vapor/src/transforms/vBind.ts
index 1379db91c..344324721 100644
--- a/packages/compiler-vapor/src/transforms/vBind.ts
+++ b/packages/compiler-vapor/src/transforms/vBind.ts
@@ -38,14 +38,6 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
}
}
- let prefix: string | undefined
- if (modifiers.includes('prop')) {
- prefix = injectPrefix(arg, '.')
- }
- if (modifiers.includes('attr')) {
- prefix = injectPrefix(arg, '^')
- }
-
if (!exp.content.trim()) {
context.options.onError(
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
@@ -64,15 +56,12 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
key: arg,
value: exp,
runtimeCamelize: camel,
- runtimePrefix: prefix,
+ modifier: modifiers.includes('prop')
+ ? '.'
+ : modifiers.includes('attr')
+ ? '^'
+ : undefined,
},
],
)
}
-
-const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => {
- if (!arg.isStatic) {
- return prefix
- }
- arg.content = prefix + arg.content
-}