From: Evan You Date: Mon, 10 Feb 2025 07:50:34 +0000 (+0800) Subject: refactor(vapor): avoid unnecessary wrapping for event handlers X-Git-Tag: v3.6.0-alpha.1~16^2~72 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a2b4289045137cf4052e2765519c57f499c98d74;p=thirdparty%2Fvuejs%2Fcore.git refactor(vapor): avoid unnecessary wrapping for event handlers --- diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index dfdee6485a..c3fa9f3b4b 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -180,7 +180,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _ctx.handleClick) + _delegate(n0, "click", e => _ctx.handleClick(e)) _renderEffect(() => { const _count = _ctx.count _setText(n0, _count, "foo", _count, "foo", _count) 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 e947310e35..6454ff1e22 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -358,8 +358,8 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _withKeys(_ctx.a, ["foo"])) - _delegate(n0, "click", () => _withKeys(_ctx.b, ["bar"])) + _delegate(n0, "click", _withKeys(e => _ctx.a(e), ["foo"])) + _delegate(n0, "click", _withKeys(e => _ctx.b(e), ["bar"])) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 21b4b1d4a3..9500b1a3eb 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -36,7 +36,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = _createFor(() => (_ctx.items), (_for_item0) => { const n2 = t0() - _delegate(n2, "click", () => $event => (_ctx.remove(_for_item0.value))) + _delegate(n2, "click", () => (_ctx.remove(_for_item0.value))) _renderEffect(() => _setText(n2, _for_item0.value)) return n2 }, (item) => (item.id)) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap index 9335d2eb75..6310362601 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap @@ -7,7 +7,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _ctx.a['b' + _ctx.c]) + _delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e)) return n0 }" `; @@ -20,7 +20,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, _ctx.event, () => _ctx.handler, { + _on(n0, _ctx.event, e => _ctx.handler(e), { effect: true }) }) @@ -36,7 +36,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, _ctx.event(_ctx.foo), () => _ctx.handler, { + _on(n0, _ctx.event(_ctx.foo), e => _ctx.handler(e), { effect: true }) }) @@ -52,7 +52,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, _ctx.event, () => _ctx.handler, { + _on(n0, _ctx.event, e => _ctx.handler(e), { effect: true }) }) @@ -91,34 +91,34 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { const n19 = t3() const n20 = t3() const n21 = t3() - _delegate(n0, "click", () => _withModifiers(_ctx.handleEvent, ["stop"])) - _on(n1, "submit", () => _withModifiers(_ctx.handleEvent, ["prevent"])) - _delegate(n2, "click", () => _withModifiers(_ctx.handleEvent, ["stop","prevent"])) - _delegate(n3, "click", () => _withModifiers(_ctx.handleEvent, ["self"])) - _on(n4, "click", () => _ctx.handleEvent, { + _delegate(n0, "click", _withModifiers(_ctx.handleEvent, ["stop"])) + _on(n1, "submit", _withModifiers(_ctx.handleEvent, ["prevent"])) + _delegate(n2, "click", _withModifiers(_ctx.handleEvent, ["stop","prevent"])) + _delegate(n3, "click", _withModifiers(_ctx.handleEvent, ["self"])) + _on(n4, "click", _ctx.handleEvent, { capture: true }) - _on(n5, "click", () => _ctx.handleEvent, { + _on(n5, "click", _ctx.handleEvent, { once: true }) - _on(n6, "scroll", () => _ctx.handleEvent, { + _on(n6, "scroll", _ctx.handleEvent, { passive: true }) - _delegate(n7, "contextmenu", () => _withModifiers(_ctx.handleEvent, ["right"])) - _delegate(n8, "click", () => _withModifiers(_ctx.handleEvent, ["left"])) - _delegate(n9, "mouseup", () => _withModifiers(_ctx.handleEvent, ["middle"])) - _delegate(n10, "contextmenu", () => _withKeys(_withModifiers(_ctx.handleEvent, ["right"]), ["enter"])) - _delegate(n11, "keyup", () => _withKeys(_ctx.handleEvent, ["enter"])) - _delegate(n12, "keyup", () => _withKeys(_ctx.handleEvent, ["tab"])) - _delegate(n13, "keyup", () => _withKeys(_ctx.handleEvent, ["delete"])) - _delegate(n14, "keyup", () => _withKeys(_ctx.handleEvent, ["esc"])) - _delegate(n15, "keyup", () => _withKeys(_ctx.handleEvent, ["space"])) - _delegate(n16, "keyup", () => _withKeys(_ctx.handleEvent, ["up"])) - _delegate(n17, "keyup", () => _withKeys(_ctx.handleEvent, ["down"])) - _delegate(n18, "keyup", () => _withKeys(_ctx.handleEvent, ["left"])) - _delegate(n19, "keyup", () => _withModifiers(_ctx.submit, ["middle"])) - _delegate(n20, "keyup", () => _withModifiers(_ctx.submit, ["middle","self"])) - _delegate(n21, "keyup", () => _withKeys(_withModifiers(_ctx.handleEvent, ["self"]), ["enter"])) + _delegate(n7, "contextmenu", _withModifiers(_ctx.handleEvent, ["right"])) + _delegate(n8, "click", _withModifiers(_ctx.handleEvent, ["left"])) + _delegate(n9, "mouseup", _withModifiers(_ctx.handleEvent, ["middle"])) + _delegate(n10, "contextmenu", _withKeys(_withModifiers(_ctx.handleEvent, ["right"]), ["enter"])) + _delegate(n11, "keyup", _withKeys(_ctx.handleEvent, ["enter"])) + _delegate(n12, "keyup", _withKeys(_ctx.handleEvent, ["tab"])) + _delegate(n13, "keyup", _withKeys(_ctx.handleEvent, ["delete"])) + _delegate(n14, "keyup", _withKeys(_ctx.handleEvent, ["esc"])) + _delegate(n15, "keyup", _withKeys(_ctx.handleEvent, ["space"])) + _delegate(n16, "keyup", _withKeys(_ctx.handleEvent, ["up"])) + _delegate(n17, "keyup", _withKeys(_ctx.handleEvent, ["down"])) + _delegate(n18, "keyup", _withKeys(_ctx.handleEvent, ["left"])) + _delegate(n19, "keyup", _withModifiers(e => _ctx.submit(e), ["middle"])) + _delegate(n20, "keyup", _withModifiers(e => _ctx.submit(e), ["middle","self"])) + _delegate(n21, "keyup", _withKeys(_withModifiers(_ctx.handleEvent, ["self"]), ["enter"])) return [n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21] }" `; @@ -130,7 +130,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => e => _ctx.foo(e)) + _delegate(n0, "click", e => _ctx.foo(e)) return n0 }" `; @@ -142,7 +142,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => (_ctx.foo($event))) + _delegate(n0, "click", $event => (_ctx.foo($event))) return n0 }" `; @@ -154,7 +154,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => {_ctx.foo($event);_ctx.bar()}) + _delegate(n0, "click", $event => {_ctx.foo($event);_ctx.bar()}) return n0 }" `; @@ -166,7 +166,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => {_ctx.i++;_ctx.foo($event)}) + _delegate(n0, "click", $event => {_ctx.i++;_ctx.foo($event)}) return n0 }" `; @@ -178,7 +178,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => (e: any): any => _ctx.foo(e)) + _delegate(n0, "click", (e: any): any => _ctx.foo(e)) return n0 }" `; @@ -190,7 +190,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => + _delegate(n0, "click", $event => { _ctx.foo($event) } @@ -206,7 +206,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => _ctx.foo($event)) + _delegate(n0, "click", $event => _ctx.foo($event)) return n0 }" `; @@ -218,7 +218,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _ctx.a['b' + _ctx.c]) + _delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e)) return n0 }" `; @@ -230,7 +230,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _ctx.test) + _delegate(n0, "click", e => _ctx.test(e)) return n0 }" `; @@ -242,7 +242,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => { + _delegate(n0, "click", () => { _ctx.foo(); _ctx.bar() }) @@ -257,7 +257,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => {_ctx.foo();_ctx.bar()}) + _delegate(n0, "click", () => {_ctx.foo();_ctx.bar()}) return n0 }" `; @@ -269,7 +269,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _ctx.foo.bar) + _delegate(n0, "click", e => _ctx.foo.bar(e)) return n0 }" `; @@ -281,7 +281,7 @@ _delegateEvents("keyup") export function render(_ctx) { const n0 = t0() - _delegate(n0, "keyup", () => _withModifiers(_ctx.test, ["exact"])) + _delegate(n0, "keyup", _withModifiers(e => _ctx.test(e), ["exact"])) return n0 }" `; @@ -293,8 +293,8 @@ _delegateEvents("click", "keyup") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => _withModifiers(_ctx.test, ["stop"])) - _delegate(n0, "keyup", () => _withKeys(_ctx.test, ["enter"])) + _delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"])) + _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["enter"])) return n0 }" `; @@ -305,7 +305,7 @@ const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() - _on(n0, "click", () => _withModifiers(_ctx.test, ["stop","prevent"]), { + _on(n0, "click", _withModifiers(e => _ctx.test(e), ["stop","prevent"]), { capture: true, once: true }) @@ -320,7 +320,7 @@ _delegateEvents("mouseup") export function render(_ctx) { const n0 = t0() - _delegate(n0, "mouseup", () => _withModifiers(_ctx.test, ["middle"])) + _delegate(n0, "mouseup", _withModifiers(e => _ctx.test(e), ["middle"])) return n0 }" `; @@ -333,7 +333,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, (_ctx.event) === "click" ? "mouseup" : (_ctx.event), () => _withModifiers(_ctx.test, ["middle"]), { + _on(n0, (_ctx.event) === "click" ? "mouseup" : (_ctx.event), _withModifiers(e => _ctx.test(e), ["middle"]), { effect: true }) }) @@ -348,7 +348,7 @@ _delegateEvents("contextmenu") export function render(_ctx) { const n0 = t0() - _delegate(n0, "contextmenu", () => _withModifiers(_ctx.test, ["right"])) + _delegate(n0, "contextmenu", _withModifiers(e => _ctx.test(e), ["right"])) return n0 }" `; @@ -361,7 +361,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, (_ctx.event) === "click" ? "contextmenu" : (_ctx.event), () => _withKeys(_withModifiers(_ctx.test, ["right"]), ["right"]), { + _on(n0, (_ctx.event) === "click" ? "contextmenu" : (_ctx.event), _withKeys(_withModifiers(e => _ctx.test(e), ["right"]), ["right"]), { effect: true }) }) @@ -376,7 +376,7 @@ _delegateEvents("click") export function render(_ctx) { const n0 = t0() - _delegate(n0, "click", () => $event => (_ctx.i++)) + _delegate(n0, "click", () => (_ctx.i++)) return n0 }" `; @@ -389,7 +389,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { - _on(n0, _ctx.e, () => _withKeys(_withModifiers(_ctx.test, ["left"]), ["left"]), { + _on(n0, _ctx.e, _withKeys(_withModifiers(e => _ctx.test(e), ["left"]), ["left"]), { effect: true }) }) @@ -402,9 +402,9 @@ exports[`v-on > should wrap in unref if identifier is setup-maybe-ref w/ inline: const n0 = t0() const n1 = t0() const n2 = t0() - _delegate(n0, "click", () => $event => (x.value=_unref(y))) - _delegate(n1, "click", () => $event => (x.value++)) - _delegate(n2, "click", () => $event => ({ x: x.value } = _unref(y))) + _delegate(n0, "click", () => (x.value=_unref(y))) + _delegate(n1, "click", () => (x.value++)) + _delegate(n2, "click", () => ({ x: x.value } = _unref(y))) return [n0, n1, n2] " `; @@ -415,7 +415,7 @@ const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() - _on(n0, "keydown", () => _withKeys(_withModifiers(_ctx.test, ["stop","ctrl"]), ["a"]), { + _on(n0, "keydown", _withKeys(_withModifiers(e => _ctx.test(e), ["stop","ctrl"]), ["a"]), { capture: true }) return n0 @@ -429,7 +429,7 @@ _delegateEvents("keyup") export function render(_ctx) { const n0 = t0() - _delegate(n0, "keyup", () => _withKeys(_ctx.test, ["left"])) + _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["left"])) return n0 }" `; @@ -441,7 +441,7 @@ _delegateEvents("click") export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() - _delegate(n0, "click", () => _ctx.handleClick) + _delegate(n0, "click", _ctx.handleClick) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts index 2a3f6b4801..b726895edd 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts @@ -165,7 +165,7 @@ describe('v-on', () => { delegate: true, }, ]) - expect(code).contains(`_delegate(n0, "click", () => $event => (_ctx.i++))`) + expect(code).contains(`_delegate(n0, "click", () => (_ctx.i++))`) }) test('should wrap in unref if identifier is setup-maybe-ref w/ inline: true', () => { @@ -182,12 +182,10 @@ describe('v-on', () => { ) expect(code).matchSnapshot() expect(helpers).contains('unref') + expect(code).contains(`_delegate(n0, "click", () => (x.value=_unref(y)))`) + expect(code).contains(`_delegate(n1, "click", () => (x.value++))`) expect(code).contains( - `_delegate(n0, "click", () => $event => (x.value=_unref(y)))`, - ) - expect(code).contains(`_delegate(n1, "click", () => $event => (x.value++))`) - expect(code).contains( - `_delegate(n2, "click", () => $event => ({ x: x.value } = _unref(y)))`, + `_delegate(n2, "click", () => ({ x: x.value } = _unref(y)))`, ) }) @@ -205,7 +203,7 @@ describe('v-on', () => { // in this case the return value is discarded and the behavior is // consistent with 2.x expect(code).contains( - `_delegate(n0, "click", () => $event => {_ctx.foo();_ctx.bar()})`, + `_delegate(n0, "click", () => {_ctx.foo();_ctx.bar()})`, ) }) @@ -223,7 +221,7 @@ describe('v-on', () => { // in this case the return value is discarded and the behavior is // consistent with 2.x expect(code).contains( - `_delegate(n0, "click", () => $event => {\n_ctx.foo();\n_ctx.bar()\n})`, + `_delegate(n0, "click", () => {\n_ctx.foo();\n_ctx.bar()\n})`, ) }) @@ -241,7 +239,7 @@ describe('v-on', () => { ]) // should NOT prefix $event expect(code).contains( - `_delegate(n0, "click", () => $event => (_ctx.foo($event)))`, + `_delegate(n0, "click", $event => (_ctx.foo($event)))`, ) }) @@ -259,7 +257,7 @@ describe('v-on', () => { ]) // should NOT prefix $event expect(code).contains( - `_delegate(n0, "click", () => $event => {_ctx.foo($event);_ctx.bar()})`, + `_delegate(n0, "click", $event => {_ctx.foo($event);_ctx.bar()})`, ) }) @@ -273,9 +271,7 @@ describe('v-on', () => { value: { content: '$event => foo($event)' }, }, ]) - expect(code).contains( - `_delegate(n0, "click", () => $event => _ctx.foo($event))`, - ) + expect(code).contains(`_delegate(n0, "click", $event => _ctx.foo($event))`) }) test('should NOT wrap as function if expression is already function expression (with Typescript)', () => { @@ -292,7 +288,7 @@ describe('v-on', () => { }, ]) expect(code).contains( - `_delegate(n0, "click", () => (e: any): any => _ctx.foo(e))`, + `_delegate(n0, "click", (e: any): any => _ctx.foo(e))`, ) }) @@ -358,7 +354,9 @@ describe('v-on', () => { ]) expect(code).matchSnapshot() - expect(code).contains(`_delegate(n0, "click", () => _ctx.a['b' + _ctx.c])`) + expect(code).contains( + `_delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e))`, + ) }) test('function expression w/ prefixIdentifiers: true', () => { @@ -373,7 +371,7 @@ describe('v-on', () => { value: { content: `e => foo(e)` }, }, ]) - expect(code).contains(`_delegate(n0, "click", () => e => _ctx.foo(e))`) + expect(code).contains(`_delegate(n0, "click", e => _ctx.foo(e))`) }) test('should error if no expression AND no modifier', () => { @@ -428,7 +426,7 @@ describe('v-on', () => { }, ]) expect(code).contains( - `_on(n0, "click", () => _withModifiers(_ctx.test, ["stop","prevent"]), { + `_on(n0, "click", _withModifiers(e => _ctx.test(e), ["stop","prevent"]), { capture: true, once: true })`, @@ -484,8 +482,8 @@ describe('v-on', () => { expect(code).matchSnapshot() expect(code).contains( - `_delegate(n0, "click", () => _withModifiers(_ctx.test, ["stop"])) - _delegate(n0, "keyup", () => _withKeys(_ctx.test, ["enter"]))`, + `_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"])) + _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["enter"]))`, ) }) @@ -668,7 +666,7 @@ describe('v-on', () => { }) expect(code).matchSnapshot() - expect(code).contains(`_delegate(n0, "click", () => _ctx.foo.bar)`) + expect(code).contains(`_delegate(n0, "click", e => _ctx.foo.bar(e))`) }) test('should delegate event', () => { diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index a010b788f8..73e23150fa 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -207,7 +207,12 @@ function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) { ...genPropKey(prop, context), ': ', ...(prop.handler - ? genEventHandler(context, prop.values[0]) + ? genEventHandler( + context, + prop.values[0], + undefined, + true /* wrap handlers passed to components */, + ) : isStatic ? ['() => (', ...values, ')'] : values), diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index c0b7e1095c..a8294bd3df 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/packages/compiler-vapor/src/generators/event.ts @@ -1,4 +1,5 @@ import { + BindingTypes, type SimpleExpressionNode, isFnExpression, isMemberExpression, @@ -88,28 +89,42 @@ export function genEventHandler( nonKeys: string[] keys: string[] } = { nonKeys: [], keys: [] }, - needWrap: boolean = true, + // passed as component prop - need additional wrap + extraWrap: boolean = false, ): CodeFragment[] { let handlerExp: CodeFragment[] = [`() => {}`] if (value && value.content.trim()) { - const isMemberExp = isMemberExpression(value, context.options) - const isInlineStatement = !( - isMemberExp || isFnExpression(value, context.options) - ) - - if (isInlineStatement) { - const expr = context.withId(() => genExpression(value, context), { - $event: null, - }) + // 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 + handlerExp = [`e => `, ...handlerExp, `(e)`] + } + } 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 = [ - '$event => ', + referencesEvent ? '$event => ' : '() => ', hasMultipleStatements ? '{' : '(', ...expr, hasMultipleStatements ? '}' : ')', ] - } else { - handlerExp = [...genExpression(value, context)] } } @@ -118,7 +133,7 @@ export function genEventHandler( handlerExp = genWithModifiers(context, handlerExp, nonKeys) if (keys.length) handlerExp = genWithKeys(context, handlerExp, keys) - if (needWrap) handlerExp.unshift(`() => `) + if (extraWrap) handlerExp.unshift(`() => `) return handlerExp } @@ -141,3 +156,15 @@ function genWithKeys( ): CodeFragment[] { return genCall(context.helper('withKeys'), handler, JSON.stringify(keys)) } + +function isConstantBinding( + value: SimpleExpressionNode, + context: CodegenContext, +) { + if (value.ast === null) { + const bindingType = context.options.bindingMetadata[value.content] + if (bindingType === BindingTypes.SETUP_CONST) { + return true + } + } +} diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index dd35dc14e5..fcbfc265d4 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -39,8 +39,6 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { let keyOverride: KeyOverride | undefined const isStaticClick = arg.isStatic && arg.content.toLowerCase() === 'click' - const delegate = - arg.isStatic && !eventOptionModifiers.length && delegatedEvents(arg.content) // normalize click.right and click.middle since they don't actually fire if (nonKeyModifiers.includes('middle')) { @@ -71,6 +69,13 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { } } + // Only delegate if: + // - no dynamic event name + // - no event option modifiers (passive, capture, once) + // - is a delegatable event + const delegate = + arg.isStatic && !eventOptionModifiers.length && delegatedEvents(arg.content) + const operation: SetEventIRNode = { type: IRNodeTypes.SET_EVENT, element: context.reference(), diff --git a/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts index ea1905086a..e07ab6c376 100644 --- a/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts +++ b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts @@ -178,7 +178,7 @@ describe('api: setup context', () => { }, setup(props, { emit }) { const n0 = template('
')() as HTMLDivElement - delegate(n0, 'click', () => () => { + delegate(n0, 'click', () => { emit('inc', props.count + 1) }) insert( diff --git a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts index 5429cc1b3f..521542144a 100644 --- a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts @@ -43,7 +43,7 @@ describe('directive: v-model', () => { () => data.value, val => (data.value = val), ) - delegate(n0, 'input', () => () => spy(data.value)) + delegate(n0, 'input', () => spy(data.value)) return n0 }).render() @@ -78,7 +78,7 @@ describe('directive: v-model', () => { () => data.value, val => (data.value = val), ) - on(n0, 'change', () => () => spy(data.value)) + on(n0, 'change', () => spy(data.value)) return n0 }).render() diff --git a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts index 2877e5c144..f7d8757c82 100644 --- a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts @@ -26,7 +26,7 @@ const createDemo = (defaultValue: boolean) => const n1 = children(n0, 0) const n2 = children(n0, 1) applyVShow(n2 as VShowElement, () => visible.value) - on(n1 as HTMLElement, 'click', () => handleClick) + on(n1 as HTMLElement, 'click', handleClick) return n0 }) diff --git a/packages/runtime-vapor/src/dom/event.ts b/packages/runtime-vapor/src/dom/event.ts index 55cfabfa19..c9c72a288e 100644 --- a/packages/runtime-vapor/src/dom/event.ts +++ b/packages/runtime-vapor/src/dom/event.ts @@ -1,4 +1,5 @@ import { onEffectCleanup } from '@vue/reactivity' +import { isArray } from '@vue/shared' export function addEventListener( el: Element, @@ -13,10 +14,9 @@ export function addEventListener( export function on( el: Element, event: string, - handlerGetter: () => undefined | ((...args: any[]) => any), + handler: (e: Event) => any, options: AddEventListenerOptions & { effect?: boolean } = {}, ): void { - const handler = eventHandler(handlerGetter) addEventListener(el, event, handler, options) if (options.effect) { onEffectCleanup(() => { @@ -28,26 +28,23 @@ export function on( export function delegate( el: any, event: string, - handlerGetter: () => undefined | ((...args: any[]) => any), + handler: (e: Event) => any, ): void { const key = `$evt${event}` - const handler = eventHandler(handlerGetter) - handler.delegate = true - ;(el[key] || (el[key] = [])).push(handler) + const existing = el[key] + if (existing) { + if (isArray(existing)) { + existing.push(handler) + } else { + el[key] = [existing, handler] + } + } else { + el[key] = handler + } } type DelegatedHandler = { (...args: any[]): any - delegate?: boolean -} - -function eventHandler( - getter: () => undefined | ((...args: any[]) => any), -): DelegatedHandler { - return (...args: any[]) => { - const handler = getter() - handler && handler(...args) - } } /** @@ -79,13 +76,19 @@ const delegatedEventHandler = (e: Event) => { }, }) while (node !== null) { - const handlers = node[`$evt${e.type}`] as DelegatedHandler[] + const handlers = node[`$evt${e.type}`] as + | DelegatedHandler + | DelegatedHandler[] if (handlers) { - for (const handler of handlers) { - if (handler.delegate && !node.disabled) { - handler(e) - if (e.cancelBubble) return + if (isArray(handlers)) { + for (const handler of handlers) { + if (!node.disabled) { + handler(e) + if (e.cancelBubble) return + } } + } else { + handlers(e) } } node = diff --git a/packages/runtime-vapor/src/dom/prop.ts b/packages/runtime-vapor/src/dom/prop.ts index 2ae558bbbc..aae3948de6 100644 --- a/packages/runtime-vapor/src/dom/prop.ts +++ b/packages/runtime-vapor/src/dom/prop.ts @@ -221,7 +221,7 @@ export function setDynamicProp( } else if (key === 'style') { setStyle(el, value) } else if (isOn(key)) { - on(el, key[2].toLowerCase() + key.slice(3), () => value, { effect: true }) + on(el, key[2].toLowerCase() + key.slice(3), value, { effect: true }) } else if ( key[0] === '.' ? ((key = key.slice(1)), true)