]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(vapor): avoid unnecessary wrapping for event handlers
authorEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 07:50:34 +0000 (15:50 +0800)
committerEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 07:50:34 +0000 (15:50 +0800)
13 files changed:
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vOn.spec.ts
packages/compiler-vapor/src/generators/component.ts
packages/compiler-vapor/src/generators/event.ts
packages/compiler-vapor/src/transforms/vOn.ts
packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
packages/runtime-vapor/__tests__/directives/vModel.spec.ts
packages/runtime-vapor/__tests__/directives/vShow.spec.ts
packages/runtime-vapor/src/dom/event.ts
packages/runtime-vapor/src/dom/prop.ts

index dfdee6485a82e771af53e6df2be314a9402ec92e..c3fa9f3b4b6025acc913cacebb5588848d99b5ee 100644 (file)
@@ -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)
index e947310e35f38b219d0e8fc0dd4ee2331ea8109f..6454ff1e22e1d8a9bfc57ed1e44ec67776d8cd53 100644 (file)
@@ -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
 }"
 `;
index 21b4b1d4a39c4667ee78b95afa1dedb6ecf86025..9500b1a3eb652737ff4764c559d0a270cf1ef895 100644 (file)
@@ -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))
index 9335d2eb75a38fb295c550808e42c8928f4225a2..6310362601edf303b8d1cc536712902767b77fcb 100644 (file)
@@ -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("<div></div>", 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("<div></div>", 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
 }"
 `;
index 2a3f6b48013a4603d5ce0de9b891f22d8d4ce15c..b726895eddde348f58261560f1c40cfa9283b07e 100644 (file)
@@ -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', () => {
index a010b788f8ecb95374048fbb7905b2c156fdb3eb..73e23150fa1d432fb317cb66225052c2ffec7afd 100644 (file)
@@ -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),
index c0b7e1095ce8dce4204fb07e2f104c7d945128ec..a8294bd3df8a466359b91c31c5595e72f9afb707 100644 (file)
@@ -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
+    }
+  }
+}
index dd35dc14e572f3bb5bae4aec6f6721697d9375f3..fcbfc265d43d710ba0f4299c13e77d5febc0d009 100644 (file)
@@ -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(),
index ea1905086aca6c0034b109aaf44b4a174a56f44b..e07ab6c3768a09e9ce91fd10c83090da41dafc0a 100644 (file)
@@ -178,7 +178,7 @@ describe('api: setup context', () => {
       },
       setup(props, { emit }) {
         const n0 = template('<div>')() as HTMLDivElement
-        delegate(n0, 'click', () => () => {
+        delegate(n0, 'click', () => {
           emit('inc', props.count + 1)
         })
         insert(
index 5429cc1b3ff12f77e38929afd348322f7dcc4d84..521542144a96fb28b827c05a9675299086e3f860 100644 (file)
@@ -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()
 
index 2877e5c144296af99aa38db5cbe4a8254317fa0b..f7d8757c826623c79db59d34b751ebd90cba62b0 100644 (file)
@@ -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
   })
 
index 55cfabfa194417e071cbf5098f86c0976124682b..c9c72a288ef54a7b3edda5465e73a191d345304d 100644 (file)
@@ -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 =
index 2ae558bbbc808ca434fa35db909541d1326b8fdf..aae3948de643f33498acffd25038d95bea6111d3 100644 (file)
@@ -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)