diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
index dd2d4abb246..65b1d3bf011 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
@@ -75,6 +75,28 @@ export function render(_ctx) {
}"
`;
+exports[`compiler: element transform > component > props merging: event handlers 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _component_Foo = _resolveComponent("Foo")
+ const n0 = _createComponentWithFallback(_component_Foo, { onClick: () => [_ctx.a, _ctx.b] }, null, true)
+ return n0
+}"
+`;
+
+exports[`compiler: element transform > component > props merging: inline event handlers 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _component_Foo = _resolveComponent("Foo")
+ const _on_click = e => _ctx.a(e)
+ const _on_click1 = e => _ctx.b(e)
+ const n0 = _createComponentWithFallback(_component_Foo, { onClick: () => [_on_click, _on_click1] }, null, true)
+ return n0
+}"
+`;
+
exports[`compiler: element transform > component > resolve component from setup bindings (inline const) 1`] = `
"
const n0 = _createComponent(Example, null, null, true)
diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
index 26a8ee06baa..c825a4fbb6e 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
@@ -328,26 +328,22 @@ describe('compiler: element transform', () => {
})
})
- test.todo('props merging: event handlers', () => {
- const { code, ir } = compileWithElementTransform(
+ test('props merging: event handlers', () => {
+ const { code } = compileWithElementTransform(
``,
)
expect(code).toMatchSnapshot()
expect(code).contains('onClick: () => [_ctx.a, _ctx.b]')
- expect(ir.block.operation).toMatchObject([
- {
- type: IRNodeTypes.CREATE_COMPONENT_NODE,
- tag: 'Foo',
- props: [
- [
- {
- key: { content: 'onClick', isStatic: true },
- values: [{ content: 'a' }, { content: 'b' }],
- },
- ],
- ],
- },
- ])
+ })
+
+ test('props merging: inline event handlers', () => {
+ const { code } = compileWithElementTransform(
+ ` a(e)" @click.bar="e => b(e)" />`,
+ )
+ expect(code).toMatchSnapshot()
+ expect(code).contains('const _on_click = e => _ctx.a(e)')
+ expect(code).contains('const _on_click1 = e => _ctx.b(e)')
+ expect(code).contains('onClick: () => [_on_click, _on_click1]')
})
test.todo('props merging: style', () => {
diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts
index 93e7f8a6ac0..b1fe3612c11 100644
--- a/packages/compiler-vapor/src/generators/component.ts
+++ b/packages/compiler-vapor/src/generators/component.ts
@@ -56,7 +56,7 @@ export function genCreateComponent(
const inlineHandlers: CodeFragment[] = handlers.reduce(
(acc, { name, value }: InlineHandler) => {
- const handler = genEventHandler(context, value, undefined, false)
+ const handler = genEventHandler(context, [value], undefined, false)
return [...acc, `const ${name} = `, ...handler, NEWLINE]
},
[],
@@ -226,7 +226,7 @@ function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
...(prop.handler
? genEventHandler(
context,
- prop.values[0],
+ prop.values,
prop.handlerModifiers,
true /* wrap handlers passed to components */,
)
diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts
index 75e06c22490..38b7ff2219b 100644
--- a/packages/compiler-vapor/src/generators/event.ts
+++ b/packages/compiler-vapor/src/generators/event.ts
@@ -31,7 +31,7 @@ export function genSetEvent(
const name = genName()
const handler = [
`${context.helper('createInvoker')}(`,
- ...genEventHandler(context, value, modifiers),
+ ...genEventHandler(context, [value], modifiers),
`)`,
]
const eventOptions = genEventOptions()
@@ -112,7 +112,7 @@ export function genSetDynamicEvents(
export function genEventHandler(
context: CodegenContext,
- value: SimpleExpressionNode | undefined,
+ values: (SimpleExpressionNode | undefined)[] | undefined,
modifiers: {
nonKeys: string[]
keys: string[]
@@ -120,49 +120,60 @@ export function genEventHandler(
// passed as component prop - need additional wrap
extraWrap: boolean = false,
): CodeFragment[] {
- let handlerExp: CodeFragment[] = [`() => {}`]
- if (value && value.content.trim()) {
- // Determine how the handler should be wrapped so it always reference the
- // latest value when invoked.
- if (isMemberExpression(value, context.options)) {
- // e.g. @click="foo.bar"
- handlerExp = genExpression(value, context)
- if (!isConstantBinding(value, context) && !extraWrap) {
- // non constant, wrap with invocation as `e => foo.bar(e)`
- // when passing as component handler, access is always dynamic so we
- // can skip this
- const isTSNode = value.ast && TS_NODE_TYPES.includes(value.ast.type)
- handlerExp = [
- `e => `,
- isTSNode ? '(' : '',
- ...handlerExp,
- isTSNode ? ')' : '',
- `(e)`,
- ]
+ let handlerExp: CodeFragment[] = []
+ if (values) {
+ values.forEach((value, index) => {
+ let exp: CodeFragment[] = []
+ if (value && value.content.trim()) {
+ // Determine how the handler should be wrapped so it always reference the
+ // latest value when invoked.
+ if (isMemberExpression(value, context.options)) {
+ // e.g. @click="foo.bar"
+ exp = genExpression(value, context)
+ if (!isConstantBinding(value, context) && !extraWrap) {
+ // non constant, wrap with invocation as `e => foo.bar(e)`
+ // when passing as component handler, access is always dynamic so we
+ // can skip this
+ const isTSNode = value.ast && TS_NODE_TYPES.includes(value.ast.type)
+ exp = [
+ `e => `,
+ isTSNode ? '(' : '',
+ ...exp,
+ isTSNode ? ')' : '',
+ `(e)`,
+ ]
+ }
+ } else if (isFnExpression(value, context.options)) {
+ // Fn expression: @click="e => foo(e)"
+ // no need to wrap in this case
+ exp = genExpression(value, context)
+ } else {
+ // inline statement
+ // @click="foo($event)" ---> $event => foo($event)
+ const referencesEvent = value.content.includes('$event')
+ const hasMultipleStatements = value.content.includes(`;`)
+ const expr = referencesEvent
+ ? context.withId(() => genExpression(value, context), {
+ $event: null,
+ })
+ : genExpression(value, context)
+ exp = [
+ referencesEvent ? '$event => ' : '() => ',
+ hasMultipleStatements ? '{' : '(',
+ ...expr,
+ hasMultipleStatements ? '}' : ')',
+ ]
+ }
+ handlerExp = handlerExp.concat([index !== 0 ? ', ' : '', ...exp])
}
- } else if (isFnExpression(value, context.options)) {
- // Fn expression: @click="e => foo(e)"
- // no need to wrap in this case
- handlerExp = genExpression(value, context)
- } else {
- // inline statement
- // @click="foo($event)" ---> $event => foo($event)
- const referencesEvent = value.content.includes('$event')
- const hasMultipleStatements = value.content.includes(`;`)
- const expr = referencesEvent
- ? context.withId(() => genExpression(value, context), {
- $event: null,
- })
- : genExpression(value, context)
- handlerExp = [
- referencesEvent ? '$event => ' : '() => ',
- hasMultipleStatements ? '{' : '(',
- ...expr,
- hasMultipleStatements ? '}' : ')',
- ]
+ })
+
+ if (values.length > 1) {
+ handlerExp = ['[', ...handlerExp, ']']
}
}
+ if (handlerExp.length === 0) handlerExp = ['() => {}']
const { keys, nonKeys } = modifiers
if (nonKeys.length)
handlerExp = genWithModifiers(context, handlerExp, nonKeys)
diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts
index 587bcf997ca..dc9c7e0aae0 100644
--- a/packages/compiler-vapor/src/transforms/transformElement.ts
+++ b/packages/compiler-vapor/src/transforms/transformElement.ts
@@ -492,7 +492,7 @@ function dedupeProperties(results: DirectiveTransformResult[]): IRProp[] {
// prop names and event handler names can be the same but serve different purposes
// e.g. `:appear="true"` is a prop while `@appear="handler"` is an event handler
if (existing && existing.handler === prop.handler) {
- if (name === 'style' || name === 'class') {
+ if (name === 'style' || name === 'class' || prop.handler) {
mergePropValues(existing, prop)
}
// unexpected duplicate, should have emitted error during parse
diff --git a/packages/runtime-vapor/src/dom/event.ts b/packages/runtime-vapor/src/dom/event.ts
index 90f7d60ed22..9dc2c8d0e7e 100644
--- a/packages/runtime-vapor/src/dom/event.ts
+++ b/packages/runtime-vapor/src/dom/event.ts
@@ -15,14 +15,18 @@ export function addEventListener(
export function on(
el: Element,
event: string,
- handler: (e: Event) => any,
+ handler: (e: Event) => any | ((e: Event) => any)[],
options: AddEventListenerOptions & { effect?: boolean } = {},
): void {
- addEventListener(el, event, handler, options)
- if (options.effect) {
- onEffectCleanup(() => {
- el.removeEventListener(event, handler, options)
- })
+ if (isArray(handler)) {
+ handler.forEach(fn => on(el, event, fn, options))
+ } else {
+ addEventListener(el, event, handler, options)
+ if (options.effect) {
+ onEffectCleanup(() => {
+ el.removeEventListener(event, handler, options)
+ })
+ }
}
}