Skip to content

Commit 62f2617

Browse files
fix(runtime-core): use consistent camelCase event casing for render functions (#2278)
close #2249
1 parent 390589e commit 62f2617

File tree

3 files changed

+42
-7
lines changed

3 files changed

+42
-7
lines changed

packages/compiler-core/src/transforms/vOn.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@ export const transformOn: DirectiveTransform = (
4242
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
4343
if (arg.isStatic) {
4444
const rawName = arg.content
45-
// for @vnode-xxx event listeners, auto convert it to camelCase
46-
const normalizedName = rawName.startsWith(`vnode`)
47-
? capitalize(camelize(rawName))
48-
: capitalize(rawName)
45+
// for all event listeners, auto convert it to camelCase. See issue #2249
46+
const normalizedName = capitalize(camelize(rawName))
4947
eventName = createSimpleExpression(`on${normalizedName}`, true, arg.loc)
5048
} else {
5149
eventName = createCompoundExpression([

packages/runtime-core/__tests__/componentEmits.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ describe('component: emit', () => {
2828
expect(onBaz).toHaveBeenCalled()
2929
})
3030

31+
test('trigger camelize event', () => {
32+
const Foo = defineComponent({
33+
render() {},
34+
created() {
35+
this.$emit('test-event')
36+
}
37+
})
38+
39+
const fooSpy = jest.fn()
40+
const Comp = () =>
41+
h(Foo, {
42+
onTestEvent: fooSpy
43+
})
44+
render(h(Comp), nodeOps.createElement('div'))
45+
46+
expect(fooSpy).toHaveBeenCalled()
47+
})
48+
3149
// for v-model:foo-bar usage in DOM templates
3250
test('trigger hyphenated events for update:xxx events', () => {
3351
const Foo = defineComponent({

packages/runtime-core/src/componentEmits.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import {
66
capitalize,
77
hyphenate,
88
isFunction,
9-
extend
9+
extend,
10+
camelize
1011
} from '@vue/shared'
1112
import {
1213
ComponentInternalInstance,
1314
ComponentOptions,
14-
ConcreteComponent
15+
ConcreteComponent,
16+
formatComponentName
1517
} from './component'
1618
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
1719
import { warn } from './warning'
@@ -78,7 +80,24 @@ export function emit(
7880
devtoolsComponentEmit(instance, event, args)
7981
}
8082

81-
let handlerName = `on${capitalize(event)}`
83+
if (__DEV__) {
84+
const lowerCaseEvent = event.toLowerCase()
85+
if (lowerCaseEvent !== event && props[`on` + capitalize(lowerCaseEvent)]) {
86+
warn(
87+
`Event "${lowerCaseEvent}" is emitted in component ` +
88+
`${formatComponentName(
89+
instance,
90+
instance.type
91+
)} but the handler is registered for "${event}". ` +
92+
`Note that HTML attributes are case-insensitive and you cannot use ` +
93+
`v-on to listen to camelCase events when using in-DOM templates. ` +
94+
`You should probably use "${hyphenate(event)}" instead of "${event}".`
95+
)
96+
}
97+
}
98+
99+
// convert handler name to camelCase. See issue #2249
100+
let handlerName = `on${capitalize(camelize(event))}`
82101
let handler = props[handlerName]
83102
// for v-model update:xxx events, also trigger kebab-case equivalent
84103
// for props passed via kebab-case

0 commit comments

Comments
 (0)