fix(runtime-core): use consistent camelCase event casing for render functions (#2278)

close #2249
This commit is contained in:
shadowings-zy 2020-10-07 06:28:56 +08:00 committed by GitHub
parent 390589ec6d
commit 62f26173ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 7 deletions

View File

@ -42,10 +42,8 @@ export const transformOn: DirectiveTransform = (
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) { if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) { if (arg.isStatic) {
const rawName = arg.content const rawName = arg.content
// for @vnode-xxx event listeners, auto convert it to camelCase // for all event listeners, auto convert it to camelCase. See issue #2249
const normalizedName = rawName.startsWith(`vnode`) const normalizedName = capitalize(camelize(rawName))
? capitalize(camelize(rawName))
: capitalize(rawName)
eventName = createSimpleExpression(`on${normalizedName}`, true, arg.loc) eventName = createSimpleExpression(`on${normalizedName}`, true, arg.loc)
} else { } else {
eventName = createCompoundExpression([ eventName = createCompoundExpression([

View File

@ -28,6 +28,24 @@ describe('component: emit', () => {
expect(onBaz).toHaveBeenCalled() expect(onBaz).toHaveBeenCalled()
}) })
test('trigger camelize event', () => {
const Foo = defineComponent({
render() {},
created() {
this.$emit('test-event')
}
})
const fooSpy = jest.fn()
const Comp = () =>
h(Foo, {
onTestEvent: fooSpy
})
render(h(Comp), nodeOps.createElement('div'))
expect(fooSpy).toHaveBeenCalled()
})
// for v-model:foo-bar usage in DOM templates // for v-model:foo-bar usage in DOM templates
test('trigger hyphenated events for update:xxx events', () => { test('trigger hyphenated events for update:xxx events', () => {
const Foo = defineComponent({ const Foo = defineComponent({

View File

@ -6,12 +6,14 @@ import {
capitalize, capitalize,
hyphenate, hyphenate,
isFunction, isFunction,
extend extend,
camelize
} from '@vue/shared' } from '@vue/shared'
import { import {
ComponentInternalInstance, ComponentInternalInstance,
ComponentOptions, ComponentOptions,
ConcreteComponent ConcreteComponent,
formatComponentName
} from './component' } from './component'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { warn } from './warning' import { warn } from './warning'
@ -78,7 +80,24 @@ export function emit(
devtoolsComponentEmit(instance, event, args) devtoolsComponentEmit(instance, event, args)
} }
let handlerName = `on${capitalize(event)}` if (__DEV__) {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && props[`on` + capitalize(lowerCaseEvent)]) {
warn(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(
instance,
instance.type
)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
// convert handler name to camelCase. See issue #2249
let handlerName = `on${capitalize(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