feat(compile-core): handle falsy dynamic args for v-on and v-bind (#2393)
fix #2388
This commit is contained in:
parent
7390487c7d
commit
052a621762
@ -71,7 +71,7 @@ describe('compiler: transform v-bind', () => {
|
|||||||
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
|
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
|
||||||
expect(props.properties[0]).toMatchObject({
|
expect(props.properties[0]).toMatchObject({
|
||||||
key: {
|
key: {
|
||||||
content: `id`,
|
content: `id || ""`,
|
||||||
isStatic: false
|
isStatic: false
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
@ -130,7 +130,7 @@ describe('compiler: transform v-bind', () => {
|
|||||||
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
|
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
|
||||||
expect(props.properties[0]).toMatchObject({
|
expect(props.properties[0]).toMatchObject({
|
||||||
key: {
|
key: {
|
||||||
content: `_${helperNameMap[CAMELIZE]}(foo)`,
|
content: `_${helperNameMap[CAMELIZE]}(foo || "")`,
|
||||||
isStatic: false
|
isStatic: false
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
@ -149,10 +149,12 @@ describe('compiler: transform v-bind', () => {
|
|||||||
key: {
|
key: {
|
||||||
children: [
|
children: [
|
||||||
`_${helperNameMap[CAMELIZE]}(`,
|
`_${helperNameMap[CAMELIZE]}(`,
|
||||||
|
`(`,
|
||||||
{ content: `_ctx.foo` },
|
{ content: `_ctx.foo` },
|
||||||
`(`,
|
`(`,
|
||||||
{ content: `_ctx.bar` },
|
{ content: `_ctx.bar` },
|
||||||
`)`,
|
`)`,
|
||||||
|
`) || ""`,
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
baseParse as parse,
|
baseParse as parse,
|
||||||
transform,
|
|
||||||
ElementNode,
|
|
||||||
ObjectExpression,
|
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
|
ElementNode,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
NodeTypes,
|
TO_HANDLER_KEY,
|
||||||
VNodeCall,
|
|
||||||
helperNameMap,
|
helperNameMap,
|
||||||
CAPITALIZE
|
NodeTypes,
|
||||||
|
ObjectExpression,
|
||||||
|
transform,
|
||||||
|
VNodeCall
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import { transformOn } from '../../src/transforms/vOn'
|
import { transformOn } from '../../src/transforms/vOn'
|
||||||
import { transformElement } from '../../src/transforms/transformElement'
|
import { transformElement } from '../../src/transforms/transformElement'
|
||||||
@ -76,7 +76,7 @@ describe('compiler: transform v-on', () => {
|
|||||||
key: {
|
key: {
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: `event` },
|
{ content: `event` },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
@ -101,7 +101,7 @@ describe('compiler: transform v-on', () => {
|
|||||||
key: {
|
key: {
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: `_ctx.event` },
|
{ content: `_ctx.event` },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
@ -126,7 +126,7 @@ describe('compiler: transform v-on', () => {
|
|||||||
key: {
|
key: {
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: `_ctx.event` },
|
{ content: `_ctx.event` },
|
||||||
`(`,
|
`(`,
|
||||||
{ content: `_ctx.foo` },
|
{ content: `_ctx.foo` },
|
||||||
|
@ -23,6 +23,7 @@ export const MERGE_PROPS = Symbol(__DEV__ ? `mergeProps` : ``)
|
|||||||
export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``)
|
export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``)
|
||||||
export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
|
export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
|
||||||
export const CAPITALIZE = Symbol(__DEV__ ? `capitalize` : ``)
|
export const CAPITALIZE = Symbol(__DEV__ ? `capitalize` : ``)
|
||||||
|
export const TO_HANDLER_KEY = Symbol(__DEV__ ? `toHandlerKey` : ``)
|
||||||
export const SET_BLOCK_TRACKING = Symbol(__DEV__ ? `setBlockTracking` : ``)
|
export const SET_BLOCK_TRACKING = Symbol(__DEV__ ? `setBlockTracking` : ``)
|
||||||
export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``)
|
export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``)
|
||||||
export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
|
export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
|
||||||
@ -56,6 +57,7 @@ export const helperNameMap: any = {
|
|||||||
[TO_HANDLERS]: `toHandlers`,
|
[TO_HANDLERS]: `toHandlers`,
|
||||||
[CAMELIZE]: `camelize`,
|
[CAMELIZE]: `camelize`,
|
||||||
[CAPITALIZE]: `capitalize`,
|
[CAPITALIZE]: `capitalize`,
|
||||||
|
[TO_HANDLER_KEY]: `toHandlerKey`,
|
||||||
[SET_BLOCK_TRACKING]: `setBlockTracking`,
|
[SET_BLOCK_TRACKING]: `setBlockTracking`,
|
||||||
[PUSH_SCOPE_ID]: `pushScopeId`,
|
[PUSH_SCOPE_ID]: `pushScopeId`,
|
||||||
[POP_SCOPE_ID]: `popScopeId`,
|
[POP_SCOPE_ID]: `popScopeId`,
|
||||||
|
@ -10,6 +10,14 @@ import { CAMELIZE } from '../runtimeHelpers'
|
|||||||
export const transformBind: DirectiveTransform = (dir, node, context) => {
|
export const transformBind: DirectiveTransform = (dir, node, context) => {
|
||||||
const { exp, modifiers, loc } = dir
|
const { exp, modifiers, loc } = dir
|
||||||
const arg = dir.arg!
|
const arg = dir.arg!
|
||||||
|
|
||||||
|
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
|
||||||
|
arg.children.unshift(`(`)
|
||||||
|
arg.children.push(`) || ""`)
|
||||||
|
} else if (!arg.isStatic) {
|
||||||
|
arg.content = `${arg.content} || ""`
|
||||||
|
}
|
||||||
|
|
||||||
// .prop is no longer necessary due to new patch behavior
|
// .prop is no longer necessary due to new patch behavior
|
||||||
// .sync is replaced by v-model:arg
|
// .sync is replaced by v-model:arg
|
||||||
if (modifiers.includes('camel')) {
|
if (modifiers.includes('camel')) {
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { DirectiveTransform, DirectiveTransformResult } from '../transform'
|
import { DirectiveTransform, DirectiveTransformResult } from '../transform'
|
||||||
import {
|
import {
|
||||||
DirectiveNode,
|
createCompoundExpression,
|
||||||
createObjectProperty,
|
createObjectProperty,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
|
DirectiveNode,
|
||||||
|
ElementTypes,
|
||||||
ExpressionNode,
|
ExpressionNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
createCompoundExpression,
|
SimpleExpressionNode
|
||||||
SimpleExpressionNode,
|
|
||||||
ElementTypes
|
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { capitalize, camelize } from '@vue/shared'
|
import { camelize, toHandlerKey } from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { processExpression } from './transformExpression'
|
import { processExpression } from './transformExpression'
|
||||||
import { validateBrowserExpression } from '../validateExpression'
|
import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { isMemberExpression, hasScopeRef } from '../utils'
|
import { hasScopeRef, isMemberExpression } from '../utils'
|
||||||
import { CAPITALIZE } from '../runtimeHelpers'
|
import { TO_HANDLER_KEY } from '../runtimeHelpers'
|
||||||
|
|
||||||
const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/
|
const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/
|
||||||
|
|
||||||
@ -43,11 +43,15 @@ export const transformOn: DirectiveTransform = (
|
|||||||
if (arg.isStatic) {
|
if (arg.isStatic) {
|
||||||
const rawName = arg.content
|
const rawName = arg.content
|
||||||
// for all event listeners, auto convert it to camelCase. See issue #2249
|
// for all event listeners, auto convert it to camelCase. See issue #2249
|
||||||
const normalizedName = capitalize(camelize(rawName))
|
eventName = createSimpleExpression(
|
||||||
eventName = createSimpleExpression(`on${normalizedName}`, true, arg.loc)
|
toHandlerKey(camelize(rawName)),
|
||||||
|
true,
|
||||||
|
arg.loc
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
// #2388
|
||||||
eventName = createCompoundExpression([
|
eventName = createCompoundExpression([
|
||||||
`"on" + ${context.helperString(CAPITALIZE)}(`,
|
`${context.helperString(TO_HANDLER_KEY)}(`,
|
||||||
arg,
|
arg,
|
||||||
`)`
|
`)`
|
||||||
])
|
])
|
||||||
@ -55,7 +59,7 @@ export const transformOn: DirectiveTransform = (
|
|||||||
} else {
|
} else {
|
||||||
// already a compound expression.
|
// already a compound expression.
|
||||||
eventName = arg
|
eventName = arg
|
||||||
eventName.children.unshift(`"on" + ${context.helperString(CAPITALIZE)}(`)
|
eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`)
|
||||||
eventName.children.push(`)`)
|
eventName.children.push(`)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
baseParse as parse,
|
baseParse as parse,
|
||||||
transform,
|
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
ObjectExpression,
|
TO_HANDLER_KEY,
|
||||||
NodeTypes,
|
|
||||||
VNodeCall,
|
|
||||||
helperNameMap,
|
helperNameMap,
|
||||||
CAPITALIZE
|
NodeTypes,
|
||||||
|
ObjectExpression,
|
||||||
|
transform,
|
||||||
|
VNodeCall
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { transformOn } from '../../src/transforms/vOn'
|
import { transformOn } from '../../src/transforms/vOn'
|
||||||
import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../../src/runtimeHelpers'
|
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers'
|
||||||
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
||||||
import { transformExpression } from '../../../compiler-core/src/transforms/transformExpression'
|
import { transformExpression } from '../../../compiler-core/src/transforms/transformExpression'
|
||||||
import { genFlagText } from '../../../compiler-core/__tests__/testUtils'
|
import { genFlagText } from '../../../compiler-core/__tests__/testUtils'
|
||||||
@ -195,14 +195,14 @@ describe('compiler-dom: transform v-on', () => {
|
|||||||
const {
|
const {
|
||||||
props: [prop2]
|
props: [prop2]
|
||||||
} = parseWithVOn(`<div @[event].right="test"/>`)
|
} = parseWithVOn(`<div @[event].right="test"/>`)
|
||||||
// ("on" + (event)).toLowerCase() === "onclick" ? "onContextmenu" : ("on" + (event))
|
// (_toHandlerKey(event)).toLowerCase() === "onclick" ? "onContextmenu" : (_toHandlerKey(event))
|
||||||
expect(prop2.key).toMatchObject({
|
expect(prop2.key).toMatchObject({
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`(`,
|
`(`,
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: 'event' },
|
{ content: 'event' },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
@ -210,7 +210,7 @@ describe('compiler-dom: transform v-on', () => {
|
|||||||
`) === "onClick" ? "onContextmenu" : (`,
|
`) === "onClick" ? "onContextmenu" : (`,
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: 'event' },
|
{ content: 'event' },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
@ -233,14 +233,14 @@ describe('compiler-dom: transform v-on', () => {
|
|||||||
const {
|
const {
|
||||||
props: [prop2]
|
props: [prop2]
|
||||||
} = parseWithVOn(`<div @[event].middle="test"/>`)
|
} = parseWithVOn(`<div @[event].middle="test"/>`)
|
||||||
// ("on" + (event)).toLowerCase() === "onclick" ? "onMouseup" : ("on" + (event))
|
// (_eventNaming(event)).toLowerCase() === "onclick" ? "onMouseup" : (_eventNaming(event))
|
||||||
expect(prop2.key).toMatchObject({
|
expect(prop2.key).toMatchObject({
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`(`,
|
`(`,
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: 'event' },
|
{ content: 'event' },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
@ -248,7 +248,7 @@ describe('compiler-dom: transform v-on', () => {
|
|||||||
`) === "onClick" ? "onMouseup" : (`,
|
`) === "onClick" ? "onMouseup" : (`,
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
`_${helperNameMap[TO_HANDLER_KEY]}(`,
|
||||||
{ content: 'event' },
|
{ content: 'event' },
|
||||||
`)`
|
`)`
|
||||||
]
|
]
|
||||||
|
@ -161,7 +161,7 @@ describe('ssr: element', () => {
|
|||||||
expect(getCompiledString(`<div v-bind:[key]="value"></div>`))
|
expect(getCompiledString(`<div v-bind:[key]="value"></div>`))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs({ [_ctx.key]: _ctx.value })
|
_ssrRenderAttrs({ [_ctx.key || \\"\\"]: _ctx.value })
|
||||||
}></div>\`"
|
}></div>\`"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ describe('ssr: element', () => {
|
|||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs({
|
_ssrRenderAttrs({
|
||||||
class: \\"foo\\",
|
class: \\"foo\\",
|
||||||
[_ctx.key]: _ctx.value
|
[_ctx.key || \\"\\"]: _ctx.value
|
||||||
})
|
})
|
||||||
}></div>\`"
|
}></div>\`"
|
||||||
`)
|
`)
|
||||||
@ -180,7 +180,7 @@ describe('ssr: element', () => {
|
|||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs({
|
_ssrRenderAttrs({
|
||||||
id: _ctx.id,
|
id: _ctx.id,
|
||||||
[_ctx.key]: _ctx.value
|
[_ctx.key || \\"\\"]: _ctx.value
|
||||||
})
|
})
|
||||||
}></div>\`"
|
}></div>\`"
|
||||||
`)
|
`)
|
||||||
@ -212,7 +212,7 @@ describe('ssr: element', () => {
|
|||||||
expect(getCompiledString(`<div :[key]="id" v-bind="obj"></div>`))
|
expect(getCompiledString(`<div :[key]="id" v-bind="obj"></div>`))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_mergeProps({ [_ctx.key]: _ctx.id }, _ctx.obj))
|
_ssrRenderAttrs(_mergeProps({ [_ctx.key || \\"\\"]: _ctx.id }, _ctx.obj))
|
||||||
}></div>\`"
|
}></div>\`"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
LifecycleHooks,
|
|
||||||
currentInstance,
|
currentInstance,
|
||||||
setCurrentInstance,
|
isInSSRComponentSetup,
|
||||||
isInSSRComponentSetup
|
LifecycleHooks,
|
||||||
|
setCurrentInstance
|
||||||
} from './component'
|
} from './component'
|
||||||
import { ComponentPublicInstance } from './componentPublicInstance'
|
import { ComponentPublicInstance } from './componentPublicInstance'
|
||||||
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
|
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { capitalize } from '@vue/shared'
|
import { toHandlerKey } from '@vue/shared'
|
||||||
import { pauseTracking, resetTracking, DebuggerEvent } from '@vue/reactivity'
|
import { DebuggerEvent, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||||
|
|
||||||
export { onActivated, onDeactivated } from './components/KeepAlive'
|
export { onActivated, onDeactivated } from './components/KeepAlive'
|
||||||
|
|
||||||
@ -49,9 +49,7 @@ export function injectHook(
|
|||||||
}
|
}
|
||||||
return wrappedHook
|
return wrappedHook
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
const apiName = `on${capitalize(
|
const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
|
||||||
ErrorTypeStrings[type].replace(/ hook$/, '')
|
|
||||||
)}`
|
|
||||||
warn(
|
warn(
|
||||||
`${apiName} is called when there is no active component instance to be ` +
|
`${apiName} is called when there is no active component instance to be ` +
|
||||||
`associated with. ` +
|
`associated with. ` +
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
isArray,
|
camelize,
|
||||||
isOn,
|
|
||||||
hasOwn,
|
|
||||||
EMPTY_OBJ,
|
EMPTY_OBJ,
|
||||||
capitalize,
|
toHandlerKey,
|
||||||
hyphenate,
|
|
||||||
isFunction,
|
|
||||||
extend,
|
extend,
|
||||||
camelize
|
hasOwn,
|
||||||
|
hyphenate,
|
||||||
|
isArray,
|
||||||
|
isFunction,
|
||||||
|
isOn
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
@ -56,10 +56,10 @@ export function emit(
|
|||||||
} = instance
|
} = instance
|
||||||
if (emitsOptions) {
|
if (emitsOptions) {
|
||||||
if (!(event in emitsOptions)) {
|
if (!(event in emitsOptions)) {
|
||||||
if (!propsOptions || !(`on` + capitalize(event) in propsOptions)) {
|
if (!propsOptions || !(toHandlerKey(event) in propsOptions)) {
|
||||||
warn(
|
warn(
|
||||||
`Component emitted event "${event}" but it is neither declared in ` +
|
`Component emitted event "${event}" but it is neither declared in ` +
|
||||||
`the emits option nor as an "on${capitalize(event)}" prop.`
|
`the emits option nor as an "${toHandlerKey(event)}" prop.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -82,7 +82,7 @@ export function emit(
|
|||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
const lowerCaseEvent = event.toLowerCase()
|
const lowerCaseEvent = event.toLowerCase()
|
||||||
if (lowerCaseEvent !== event && props[`on` + capitalize(lowerCaseEvent)]) {
|
if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) {
|
||||||
warn(
|
warn(
|
||||||
`Event "${lowerCaseEvent}" is emitted in component ` +
|
`Event "${lowerCaseEvent}" is emitted in component ` +
|
||||||
`${formatComponentName(
|
`${formatComponentName(
|
||||||
@ -97,12 +97,12 @@ export function emit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert handler name to camelCase. See issue #2249
|
// convert handler name to camelCase. See issue #2249
|
||||||
let handlerName = `on${capitalize(camelize(event))}`
|
let handlerName = toHandlerKey(camelize(event))
|
||||||
let handler = props[handlerName]
|
let handler = props[handlerName]
|
||||||
// for v-model update:xxx events, also trigger kebab-case equivalent
|
// for v-model update:xxx events, also trigger kebab-case equivalent
|
||||||
// for props passed via kebab-case
|
// for props passed via kebab-case
|
||||||
if (!handler && event.startsWith('update:')) {
|
if (!handler && event.startsWith('update:')) {
|
||||||
handlerName = `on${capitalize(hyphenate(event))}`
|
handlerName = toHandlerKey(hyphenate(event))
|
||||||
handler = props[handlerName]
|
handler = props[handlerName]
|
||||||
}
|
}
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isObject, capitalize } from '@vue/shared'
|
import { toHandlerKey, isObject } from '@vue/shared'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,7 +12,7 @@ export function toHandlers(obj: Record<string, any>): Record<string, any> {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
ret[`on${capitalize(key)}`] = obj[key]
|
ret[toHandlerKey(key)] = obj[key]
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,12 @@ export {
|
|||||||
createCommentVNode,
|
createCommentVNode,
|
||||||
createStaticVNode
|
createStaticVNode
|
||||||
} from './vnode'
|
} from './vnode'
|
||||||
export { toDisplayString, camelize, capitalize } from '@vue/shared'
|
export {
|
||||||
|
toDisplayString,
|
||||||
|
camelize,
|
||||||
|
capitalize,
|
||||||
|
toHandlerKey
|
||||||
|
} from '@vue/shared'
|
||||||
|
|
||||||
// For test-utils
|
// For test-utils
|
||||||
export { transformVNodeArgs } from './vnode'
|
export { transformVNodeArgs } from './vnode'
|
||||||
|
@ -1070,6 +1070,7 @@ function baseCreateRenderer(
|
|||||||
) => {
|
) => {
|
||||||
if (oldProps !== newProps) {
|
if (oldProps !== newProps) {
|
||||||
for (const key in newProps) {
|
for (const key in newProps) {
|
||||||
|
// empty string is not valid prop
|
||||||
if (isReservedProp(key)) continue
|
if (isReservedProp(key)) continue
|
||||||
const next = newProps[key]
|
const next = newProps[key]
|
||||||
const prev = oldProps[key]
|
const prev = oldProps[key]
|
||||||
|
@ -644,7 +644,7 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
|
|||||||
? [].concat(existing as any, toMerge[key] as any)
|
? [].concat(existing as any, toMerge[key] as any)
|
||||||
: incoming
|
: incoming
|
||||||
}
|
}
|
||||||
} else {
|
} else if (key !== '') {
|
||||||
ret[key] = toMerge[key]
|
ret[key] = toMerge[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ import {
|
|||||||
makeMap
|
makeMap
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
|
|
||||||
const shouldIgnoreProp = makeMap(`key,ref,innerHTML,textContent`)
|
// leading comma for empty string ""
|
||||||
|
const shouldIgnoreProp = makeMap(`,key,ref,innerHTML,textContent`)
|
||||||
|
|
||||||
export function ssrRenderAttrs(
|
export function ssrRenderAttrs(
|
||||||
props: Record<string, unknown>,
|
props: Record<string, unknown>,
|
||||||
|
@ -94,7 +94,8 @@ export const isIntegerKey = (key: unknown) =>
|
|||||||
'' + parseInt(key, 10) === key
|
'' + parseInt(key, 10) === key
|
||||||
|
|
||||||
export const isReservedProp = /*#__PURE__*/ makeMap(
|
export const isReservedProp = /*#__PURE__*/ makeMap(
|
||||||
'key,ref,' +
|
// the leading comma is intentional so empty string "" is also included
|
||||||
|
',key,ref,' +
|
||||||
'onVnodeBeforeMount,onVnodeMounted,' +
|
'onVnodeBeforeMount,onVnodeMounted,' +
|
||||||
'onVnodeBeforeUpdate,onVnodeUpdated,' +
|
'onVnodeBeforeUpdate,onVnodeUpdated,' +
|
||||||
'onVnodeBeforeUnmount,onVnodeUnmounted'
|
'onVnodeBeforeUnmount,onVnodeUnmounted'
|
||||||
@ -122,19 +123,22 @@ const hyphenateRE = /\B([A-Z])/g
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export const hyphenate = cacheStringFunction(
|
export const hyphenate = cacheStringFunction((str: string) =>
|
||||||
(str: string): string => {
|
str.replace(hyphenateRE, '-$1').toLowerCase()
|
||||||
return str.replace(hyphenateRE, '-$1').toLowerCase()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export const capitalize = cacheStringFunction(
|
export const capitalize = cacheStringFunction(
|
||||||
(str: string): string => {
|
(str: string) => str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
)
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export const toHandlerKey = cacheStringFunction(
|
||||||
|
(str: string) => (str ? `on${capitalize(str)}` : ``)
|
||||||
)
|
)
|
||||||
|
|
||||||
// compare whether a value has changed, accounting for NaN.
|
// compare whether a value has changed, accounting for NaN.
|
||||||
|
Loading…
Reference in New Issue
Block a user