feat: support v-bind .prop & .attr modifiers

Also allows render function usage like the following:

```js
h({
  '.prop': 1, // force set as property
  '^attr': 'foo' // force set as attribute
})
```
This commit is contained in:
Evan You
2021-07-13 15:58:18 -04:00
parent 00f0b3c465
commit 1c7d737cc8
9 changed files with 279 additions and 60 deletions

View File

@@ -1276,6 +1276,54 @@ describe('compiler: parse', () => {
})
})
test('v-bind .prop shorthand', () => {
const ast = baseParse('<div .a=b />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
type: NodeTypes.DIRECTIVE,
name: 'bind',
arg: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a',
isStatic: true,
constType: ConstantTypes.CAN_STRINGIFY,
loc: {
source: 'a',
start: {
column: 7,
line: 1,
offset: 6
},
end: {
column: 8,
line: 1,
offset: 7
}
}
},
modifiers: ['prop'],
exp: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b',
isStatic: false,
constType: ConstantTypes.NOT_CONSTANT,
loc: {
start: { offset: 8, line: 1, column: 9 },
end: { offset: 9, line: 1, column: 10 },
source: 'b'
}
},
loc: {
start: { offset: 5, line: 1, column: 6 },
end: { offset: 9, line: 1, column: 10 },
source: '.a=b'
}
})
})
test('v-bind shorthand with modifier', () => {
const ast = baseParse('<div :a.sync=b />')
const directive = (ast.children[0] as ElementNode).props[0]

View File

@@ -172,22 +172,140 @@ describe('compiler: transform v-bind', () => {
const node = parseWithVBind(`<div v-bind:[foo(bar)].camel="id"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as VNodeCall).props as CallExpression
expect(props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: NORMALIZE_PROPS,
arguments: [
{
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
key: {
children: [
`_${helperNameMap[CAMELIZE]}(`,
`(`,
{ content: `_ctx.foo` },
`(`,
{ content: `_ctx.bar` },
`)`,
`) || ""`,
`)`
]
},
value: {
content: `_ctx.id`,
isStatic: false
}
}
]
}
]
})
})
test('.prop modifier', () => {
const node = parseWithVBind(`<div v-bind:fooBar.prop="id"/>`)
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
children: [
`_${helperNameMap[CAMELIZE]}(`,
`(`,
{ content: `_ctx.foo` },
`(`,
{ content: `_ctx.bar` },
`)`,
`) || ""`,
`)`
]
content: `.fooBar`,
isStatic: true
},
value: {
content: `_ctx.id`,
content: `id`,
isStatic: false
}
})
})
test('.prop modifier w/ dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[fooBar].prop="id"/>`)
const props = (node.codegenNode as VNodeCall).props as CallExpression
expect(props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: NORMALIZE_PROPS,
arguments: [
{
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
key: {
content: '`.${fooBar || ""}`',
isStatic: false
},
value: {
content: `id`,
isStatic: false
}
}
]
}
]
})
})
test('.prop modifier w/ dynamic arg + prefixIdentifiers', () => {
const node = parseWithVBind(`<div v-bind:[foo(bar)].prop="id"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as VNodeCall).props as CallExpression
expect(props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: NORMALIZE_PROPS,
arguments: [
{
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
key: {
children: [
`'.' + (`,
`(`,
{ content: `_ctx.foo` },
`(`,
{ content: `_ctx.bar` },
`)`,
`) || ""`,
`)`
]
},
value: {
content: `_ctx.id`,
isStatic: false
}
}
]
}
]
})
})
test('.prop modifier (shorthand)', () => {
const node = parseWithVBind(`<div .fooBar="id"/>`)
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `.fooBar`,
isStatic: true
},
value: {
content: `id`,
isStatic: false
}
})
})
test('.attr modifier', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.attr="id"/>`)
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `^foo-bar`,
isStatic: true
},
value: {
content: `id`,
isStatic: false
}
})