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:
@@ -221,6 +221,7 @@ export interface SimpleExpressionNode extends Node {
|
||||
* the identifiers declared inside the function body.
|
||||
*/
|
||||
identifiers?: string[]
|
||||
isHandlerKey?: boolean
|
||||
}
|
||||
|
||||
export interface InterpolationNode extends Node {
|
||||
@@ -243,6 +244,7 @@ export interface CompoundExpressionNode extends Node {
|
||||
* the identifiers declared inside the function body.
|
||||
*/
|
||||
identifiers?: string[]
|
||||
isHandlerKey?: boolean
|
||||
}
|
||||
|
||||
export interface IfNode extends Node {
|
||||
|
||||
@@ -772,14 +772,19 @@ function parseAttribute(
|
||||
}
|
||||
const loc = getSelection(context, start)
|
||||
|
||||
if (!context.inVPre && /^(v-|:|@|#)/.test(name)) {
|
||||
const match = /(?:^v-([a-z0-9-]+))?(?:(?::|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i.exec(
|
||||
if (!context.inVPre && /^(v-|:|\.|@|#)/.test(name)) {
|
||||
const match = /(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i.exec(
|
||||
name
|
||||
)!
|
||||
|
||||
let isPropShorthand = startsWith(name, '.')
|
||||
let dirName =
|
||||
match[1] ||
|
||||
(startsWith(name, ':') ? 'bind' : startsWith(name, '@') ? 'on' : 'slot')
|
||||
(isPropShorthand || startsWith(name, ':')
|
||||
? 'bind'
|
||||
: startsWith(name, '@')
|
||||
? 'on'
|
||||
: 'slot')
|
||||
let arg: ExpressionNode | undefined
|
||||
|
||||
if (match[2]) {
|
||||
@@ -835,6 +840,7 @@ function parseAttribute(
|
||||
}
|
||||
|
||||
const modifiers = match[3] ? match[3].substr(1).split('.') : []
|
||||
if (isPropShorthand) modifiers.push('prop')
|
||||
|
||||
// 2.x compat v-bind:foo.sync -> v-model:foo
|
||||
if (__COMPAT__ && dirName === 'bind' && arg) {
|
||||
|
||||
@@ -700,21 +700,26 @@ export function buildProps(
|
||||
// but still need to deal with dynamic key binding
|
||||
let classKeyIndex = -1
|
||||
let styleKeyIndex = -1
|
||||
let dynamicKeyIndex = -1
|
||||
let hasDynamicKey = false
|
||||
|
||||
for (let i = 0; i < propsExpression.properties.length; i++) {
|
||||
const p = propsExpression.properties[i]
|
||||
if (p.key.type !== NodeTypes.SIMPLE_EXPRESSION) continue
|
||||
if (!isStaticExp(p.key)) dynamicKeyIndex = i
|
||||
if (isStaticExp(p.key) && p.key.content === 'class') classKeyIndex = i
|
||||
if (isStaticExp(p.key) && p.key.content === 'style') styleKeyIndex = i
|
||||
const key = propsExpression.properties[i].key
|
||||
if (isStaticExp(key)) {
|
||||
if (key.content === 'class') {
|
||||
classKeyIndex = i
|
||||
} else if (key.content === 'style') {
|
||||
styleKeyIndex = i
|
||||
}
|
||||
} else if (!key.isHandlerKey) {
|
||||
hasDynamicKey = true
|
||||
}
|
||||
}
|
||||
|
||||
const classProp = propsExpression.properties[classKeyIndex]
|
||||
const styleProp = propsExpression.properties[styleKeyIndex]
|
||||
|
||||
// no dynamic key
|
||||
if (dynamicKeyIndex === -1) {
|
||||
if (!hasDynamicKey) {
|
||||
if (classProp && !isStaticExp(classProp.value)) {
|
||||
classProp.value = createCallExpression(
|
||||
context.helper(NORMALIZE_CLASS),
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { DirectiveTransform } from '../transform'
|
||||
import { createObjectProperty, createSimpleExpression, NodeTypes } from '../ast'
|
||||
import {
|
||||
createObjectProperty,
|
||||
createSimpleExpression,
|
||||
ExpressionNode,
|
||||
NodeTypes
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { camelize } from '@vue/shared'
|
||||
import { CAMELIZE } from '../runtimeHelpers'
|
||||
@@ -18,7 +23,6 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||
arg.content = `${arg.content} || ""`
|
||||
}
|
||||
|
||||
// .prop is no longer necessary due to new patch behavior
|
||||
// .sync is replaced by v-model:arg
|
||||
if (modifiers.includes('camel')) {
|
||||
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||
@@ -33,6 +37,14 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (modifiers.includes('prop')) {
|
||||
injectPrefix(arg, '.')
|
||||
}
|
||||
|
||||
if (modifiers.includes('attr')) {
|
||||
injectPrefix(arg, '^')
|
||||
}
|
||||
|
||||
if (
|
||||
!exp ||
|
||||
(exp.type === NodeTypes.SIMPLE_EXPRESSION && !exp.content.trim())
|
||||
@@ -47,3 +59,16 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||
props: [createObjectProperty(arg!, exp)]
|
||||
}
|
||||
}
|
||||
|
||||
const injectPrefix = (arg: ExpressionNode, prefix: string) => {
|
||||
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||
if (arg.isStatic) {
|
||||
arg.content = prefix + arg.content
|
||||
} else {
|
||||
arg.content = `\`${prefix}\${${arg.content}}\``
|
||||
}
|
||||
} else {
|
||||
arg.children.unshift(`'${prefix}' + (`)
|
||||
arg.children.push(`)`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,5 +163,7 @@ export const transformOn: DirectiveTransform = (
|
||||
ret.props[0].value = context.cache(ret.props[0].value)
|
||||
}
|
||||
|
||||
// mark the key as handler for props normalization check
|
||||
ret.props.forEach(p => (p.key.isHandlerKey = true))
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user