]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(vapor): generate simpler evenet delegation code when possible
authorEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 08:17:25 +0000 (16:17 +0800)
committerEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 08:17:25 +0000 (16:17 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.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/event.ts

index c3fa9f3b4b6025acc913cacebb5588848d99b5ee..c8c9d254d62d85d24b784d06f5564e08708d1724 100644 (file)
@@ -174,13 +174,13 @@ export function render(_ctx) {
 `;
 
 exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { delegate as _delegate, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<button></button>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.handleClick(e))
+  n0.$evtclick = e => _ctx.handleClick(e)
   _renderEffect(() => {
     const _count = _ctx.count
     _setText(n0, _count, "foo", _count, "foo", _count)
index 9500b1a3eb652737ff4764c559d0a270cf1ef895..a26a0f5f018571513d16c01627e762675f546887 100644 (file)
@@ -29,14 +29,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > basic v-for 1`] = `
-"import { delegate as _delegate, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
     const n2 = t0()
-    _delegate(n2, "click", () => (_ctx.remove(_for_item0.value)))
+    n2.$evtclick = () => (_ctx.remove(_for_item0.value))
     _renderEffect(() => _setText(n2, _for_item0.value))
     return n2
   }, (item) => (item.id))
index 6310362601edf303b8d1cc536712902767b77fcb..cb1a05d2a432d6e9a7ceb2dd42dc11f610a26644 100644 (file)
@@ -1,13 +1,13 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`v-on > complex member expression w/ prefixIdentifiers: true 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e))
+  n0.$evtclick = e => _ctx.a['b' + _ctx.c](e)
   return n0
 }"
 `;
@@ -61,7 +61,7 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > event modifier 1`] = `
-"import { withModifiers as _withModifiers, delegate as _delegate, on as _on, withKeys as _withKeys, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withModifiers as _withModifiers, on as _on, withKeys as _withKeys, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<a></a>")
 const t1 = _template("<form></form>")
 const t2 = _template("<div></div>")
@@ -91,10 +91,10 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n19 = t3()
   const n20 = t3()
   const n21 = t3()
-  _delegate(n0, "click", _withModifiers(_ctx.handleEvent, ["stop"]))
+  n0.$evtclick = _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"]))
+  n2.$evtclick = _withModifiers(_ctx.handleEvent, ["stop","prevent"])
+  n3.$evtclick = _withModifiers(_ctx.handleEvent, ["self"])
   _on(n4, "click", _ctx.handleEvent, {
     capture: true
   })
@@ -104,197 +104,197 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   _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(e => _ctx.submit(e), ["middle"]))
-  _delegate(n20, "keyup", _withModifiers(e => _ctx.submit(e), ["middle","self"]))
-  _delegate(n21, "keyup", _withKeys(_withModifiers(_ctx.handleEvent, ["self"]), ["enter"]))
+  n7.$evtcontextmenu = _withModifiers(_ctx.handleEvent, ["right"])
+  n8.$evtclick = _withModifiers(_ctx.handleEvent, ["left"])
+  n9.$evtmouseup = _withModifiers(_ctx.handleEvent, ["middle"])
+  n10.$evtcontextmenu = _withKeys(_withModifiers(_ctx.handleEvent, ["right"]), ["enter"])
+  n11.$evtkeyup = _withKeys(_ctx.handleEvent, ["enter"])
+  n12.$evtkeyup = _withKeys(_ctx.handleEvent, ["tab"])
+  n13.$evtkeyup = _withKeys(_ctx.handleEvent, ["delete"])
+  n14.$evtkeyup = _withKeys(_ctx.handleEvent, ["esc"])
+  n15.$evtkeyup = _withKeys(_ctx.handleEvent, ["space"])
+  n16.$evtkeyup = _withKeys(_ctx.handleEvent, ["up"])
+  n17.$evtkeyup = _withKeys(_ctx.handleEvent, ["down"])
+  n18.$evtkeyup = _withKeys(_ctx.handleEvent, ["left"])
+  n19.$evtkeyup = _withModifiers(e => _ctx.submit(e), ["middle"])
+  n20.$evtkeyup = _withModifiers(e => _ctx.submit(e), ["middle","self"])
+  n21.$evtkeyup = _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]
 }"
 `;
 
 exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.foo(e))
+  n0.$evtclick = e => _ctx.foo(e)
   return n0
 }"
 `;
 
 exports[`v-on > inline statement w/ prefixIdentifiers: true 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", $event => (_ctx.foo($event)))
+  n0.$evtclick = $event => (_ctx.foo($event))
   return n0
 }"
 `;
 
 exports[`v-on > multiple inline statements w/ prefixIdentifiers: true 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", $event => {_ctx.foo($event);_ctx.bar()})
+  n0.$evtclick = $event => {_ctx.foo($event);_ctx.bar()}
   return n0
 }"
 `;
 
 exports[`v-on > should NOT add a prefix to $event if the expression is a function expression 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", $event => {_ctx.i++;_ctx.foo($event)})
+  n0.$evtclick = $event => {_ctx.i++;_ctx.foo($event)}
   return n0
 }"
 `;
 
 exports[`v-on > should NOT wrap as function if expression is already function expression (with Typescript) 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", (e: any): any => _ctx.foo(e))
+  n0.$evtclick = (e: any): any => _ctx.foo(e)
   return n0
 }"
 `;
 
 exports[`v-on > should NOT wrap as function if expression is already function expression (with newlines) 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", 
+  n0.$evtclick = 
       $event => {
         _ctx.foo($event)
       }
-    )
+    
   return n0
 }"
 `;
 
 exports[`v-on > should NOT wrap as function if expression is already function expression 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", $event => _ctx.foo($event))
+  n0.$evtclick = $event => _ctx.foo($event)
   return n0
 }"
 `;
 
 exports[`v-on > should NOT wrap as function if expression is complex member expression 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e))
+  n0.$evtclick = e => _ctx.a['b' + _ctx.c](e)
   return n0
 }"
 `;
 
 exports[`v-on > should delegate event 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.test(e))
+  n0.$evtclick = e => _ctx.test(e)
   return n0
 }"
 `;
 
 exports[`v-on > should handle multi-line statement 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", () => {
+  n0.$evtclick = () => {
 _ctx.foo();
 _ctx.bar()
-})
+}
   return n0
 }"
 `;
 
 exports[`v-on > should handle multiple inline statement 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", () => {_ctx.foo();_ctx.bar()})
+  n0.$evtclick = () => {_ctx.foo();_ctx.bar()}
   return n0
 }"
 `;
 
 exports[`v-on > should not prefix member expression 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", e => _ctx.foo.bar(e))
+  n0.$evtclick = e => _ctx.foo.bar(e)
   return n0
 }"
 `;
 
 exports[`v-on > should not wrap keys guard if no key modifier is present 1`] = `
-"import { withModifiers as _withModifiers, delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withModifiers as _withModifiers, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("keyup")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "keyup", _withModifiers(e => _ctx.test(e), ["exact"]))
+  n0.$evtkeyup = _withModifiers(e => _ctx.test(e), ["exact"])
   return n0
 }"
 `;
 
 exports[`v-on > should support multiple events and modifiers options w/ prefixIdentifiers: true 1`] = `
-"import { withModifiers as _withModifiers, delegate as _delegate, withKeys as _withKeys, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withModifiers as _withModifiers, withKeys as _withKeys, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click", "keyup")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))
-  _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["enter"]))
+  n0.$evtclick = _withModifiers(e => _ctx.test(e), ["stop"])
+  n0.$evtkeyup = _withKeys(e => _ctx.test(e), ["enter"])
   return n0
 }"
 `;
@@ -314,13 +314,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should transform click.middle 1`] = `
-"import { withModifiers as _withModifiers, delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withModifiers as _withModifiers, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("mouseup")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "mouseup", _withModifiers(e => _ctx.test(e), ["middle"]))
+  n0.$evtmouseup = _withModifiers(e => _ctx.test(e), ["middle"])
   return n0
 }"
 `;
@@ -342,13 +342,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should transform click.right 1`] = `
-"import { withModifiers as _withModifiers, delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withModifiers as _withModifiers, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("contextmenu")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "contextmenu", _withModifiers(e => _ctx.test(e), ["right"]))
+  n0.$evtcontextmenu = _withModifiers(e => _ctx.test(e), ["right"])
   return n0
 }"
 `;
@@ -369,14 +369,27 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`v-on > should use delegate helper when have multiple events of same name 1`] = `
+"import { delegate as _delegate, withModifiers as _withModifiers, delegateEvents as _delegateEvents, template as _template } from 'vue';
+const t0 = _template("<div></div>", true)
+_delegateEvents("click")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _delegate(n0, "click", e => _ctx.test(e))
+  _delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))
+  return n0
+}"
+`;
+
 exports[`v-on > should wrap as function if expression is inline statement 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "click", () => (_ctx.i++))
+  n0.$evtclick = () => (_ctx.i++)
   return n0
 }"
 `;
@@ -402,9 +415,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", () => (x.value=_unref(y)))
-  _delegate(n1, "click", () => (x.value++))
-  _delegate(n2, "click", () => ({ x: x.value } = _unref(y)))
+  n0.$evtclick = () => (x.value=_unref(y))
+  n1.$evtclick = () => (x.value++)
+  n2.$evtclick = () => ({ x: x.value } = _unref(y))
   return [n0, n1, n2]
 "
 `;
@@ -423,25 +436,25 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should wrap keys guard for static key event w/ left/right modifiers 1`] = `
-"import { withKeys as _withKeys, delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { withKeys as _withKeys, delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("keyup")
 
 export function render(_ctx) {
   const n0 = t0()
-  _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["left"]))
+  n0.$evtkeyup = _withKeys(e => _ctx.test(e), ["left"])
   return n0
 }"
 `;
 
 exports[`v-on > simple expression 1`] = `
-"import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue';
+"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
 _delegateEvents("click")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _delegate(n0, "click", _ctx.handleClick)
+  n0.$evtclick = _ctx.handleClick
   return n0
 }"
 `;
index b726895eddde348f58261560f1c40cfa9283b07e..1ce2169366c0ff2b005310761f895054f304a2ff 100644 (file)
@@ -26,7 +26,7 @@ describe('v-on', () => {
     )
 
     expect(code).matchSnapshot()
-    expect(helpers).contains('delegate')
+    expect(helpers).not.contains('delegate') // optimized as direct attachment
     expect(ir.block.effect).toEqual([])
     expect(ir.block.operation).toMatchObject([
       {
@@ -151,7 +151,7 @@ describe('v-on', () => {
     const { code, ir, helpers } = compileWithVOn(`<div @click="i++"/>`)
 
     expect(code).matchSnapshot()
-    expect(helpers).contains('delegate')
+    expect(helpers).not.contains('delegate')
     expect(ir.block.effect).toEqual([])
     expect(ir.block.operation).toMatchObject([
       {
@@ -165,7 +165,7 @@ describe('v-on', () => {
         delegate: true,
       },
     ])
-    expect(code).contains(`_delegate(n0, "click", () => (_ctx.i++))`)
+    expect(code).contains(`n0.$evtclick = () => (_ctx.i++)`)
   })
 
   test('should wrap in unref if identifier is setup-maybe-ref w/ inline: true', () => {
@@ -182,11 +182,9 @@ 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(n2, "click", () => ({ x: x.value } = _unref(y)))`,
-    )
+    expect(code).contains(`n0.$evtclick = () => (x.value=_unref(y))`)
+    expect(code).contains(`n1.$evtclick = () => (x.value++)`)
+    expect(code).contains(`n2.$evtclick = () => ({ x: x.value } = _unref(y))`)
   })
 
   test('should handle multiple inline statement', () => {
@@ -202,9 +200,7 @@ describe('v-on', () => {
     // should wrap with `{` for multiple statements
     // in this case the return value is discarded and the behavior is
     // consistent with 2.x
-    expect(code).contains(
-      `_delegate(n0, "click", () => {_ctx.foo();_ctx.bar()})`,
-    )
+    expect(code).contains(`n0.$evtclick = () => {_ctx.foo();_ctx.bar()}`)
   })
 
   test('should handle multi-line statement', () => {
@@ -220,9 +216,7 @@ describe('v-on', () => {
     // should wrap with `{` for multiple statements
     // in this case the return value is discarded and the behavior is
     // consistent with 2.x
-    expect(code).contains(
-      `_delegate(n0, "click", () => {\n_ctx.foo();\n_ctx.bar()\n})`,
-    )
+    expect(code).contains(`n0.$evtclick = () => {\n_ctx.foo();\n_ctx.bar()\n}`)
   })
 
   test('inline statement w/ prefixIdentifiers: true', () => {
@@ -238,9 +232,7 @@ describe('v-on', () => {
       },
     ])
     // should NOT prefix $event
-    expect(code).contains(
-      `_delegate(n0, "click", $event => (_ctx.foo($event)))`,
-    )
+    expect(code).contains(`n0.$evtclick = $event => (_ctx.foo($event))`)
   })
 
   test('multiple inline statements w/ prefixIdentifiers: true', () => {
@@ -257,7 +249,7 @@ describe('v-on', () => {
     ])
     // should NOT prefix $event
     expect(code).contains(
-      `_delegate(n0, "click", $event => {_ctx.foo($event);_ctx.bar()})`,
+      `n0.$evtclick = $event => {_ctx.foo($event);_ctx.bar()}`,
     )
   })
 
@@ -271,7 +263,7 @@ describe('v-on', () => {
         value: { content: '$event => foo($event)' },
       },
     ])
-    expect(code).contains(`_delegate(n0, "click", $event => _ctx.foo($event))`)
+    expect(code).contains(`n0.$evtclick = $event => _ctx.foo($event)`)
   })
 
   test('should NOT wrap as function if expression is already function expression (with Typescript)', () => {
@@ -287,9 +279,7 @@ describe('v-on', () => {
         value: { content: '(e: any): any => foo(e)' },
       },
     ])
-    expect(code).contains(
-      `_delegate(n0, "click", (e: any): any => _ctx.foo(e))`,
-    )
+    expect(code).contains(`n0.$evtclick = (e: any): any => _ctx.foo(e)`)
   })
 
   test('should NOT wrap as function if expression is already function expression (with newlines)', () => {
@@ -354,9 +344,7 @@ describe('v-on', () => {
     ])
 
     expect(code).matchSnapshot()
-    expect(code).contains(
-      `_delegate(n0, "click", e => _ctx.a['b' + _ctx.c](e))`,
-    )
+    expect(code).contains(`n0.$evtclick = e => _ctx.a['b' + _ctx.c](e)`)
   })
 
   test('function expression w/ prefixIdentifiers: true', () => {
@@ -371,7 +359,7 @@ describe('v-on', () => {
         value: { content: `e => foo(e)` },
       },
     ])
-    expect(code).contains(`_delegate(n0, "click", e => _ctx.foo(e))`)
+    expect(code).contains(`n0.$evtclick = e => _ctx.foo(e)`)
   })
 
   test('should error if no expression AND no modifier', () => {
@@ -482,8 +470,8 @@ describe('v-on', () => {
 
     expect(code).matchSnapshot()
     expect(code).contains(
-      `_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))
-  _delegate(n0, "keyup", _withKeys(e => _ctx.test(e), ["enter"]))`,
+      `n0.$evtclick = _withModifiers(e => _ctx.test(e), ["stop"])
+  n0.$evtkeyup = _withKeys(e => _ctx.test(e), ["enter"])`,
     )
   })
 
@@ -666,7 +654,7 @@ describe('v-on', () => {
     })
 
     expect(code).matchSnapshot()
-    expect(code).contains(`_delegate(n0, "click", e => _ctx.foo.bar(e))`)
+    expect(code).contains(`n0.$evtclick = e => _ctx.foo.bar(e)`)
   })
 
   test('should delegate event', () => {
@@ -682,4 +670,16 @@ describe('v-on', () => {
       },
     ])
   })
+
+  test('should use delegate helper when have multiple events of same name', () => {
+    const { code, helpers } = compileWithVOn(
+      `<div @click="test" @click.stop="test" />`,
+    )
+    expect(helpers).contains('delegate')
+    expect(code).toMatchSnapshot()
+    expect(code).contains('_delegate(n0, "click", e => _ctx.test(e))')
+    expect(code).contains(
+      '_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))',
+    )
+  })
 })
index a8294bd3df8a466359b91c31c5595e72f9afb707..cfb47b6118456a788a78f59478a7664a40543d37 100644 (file)
@@ -5,7 +5,12 @@ import {
   isMemberExpression,
 } from '@vue/compiler-dom'
 import type { CodegenContext } from '../generate'
-import type { SetDynamicEventsIRNode, SetEventIRNode } from '../ir'
+import {
+  IRNodeTypes,
+  type OperationNode,
+  type SetDynamicEventsIRNode,
+  type SetEventIRNode,
+} from '../ir'
 import { genExpression } from './expression'
 import {
   type CodeFragment,
@@ -29,6 +34,12 @@ export function genSetEvent(
   if (delegate) {
     // key is static
     context.delegates.add(key.content)
+    // if this is the only delegated event of this name on this element,
+    // we can generate optimized handler attachment code
+    // e.g. n1.$evtclick = () => {}
+    if (!context.block.operation.some(isSameDelegateEvent)) {
+      return [NEWLINE, `n${element}.$evt${key.content} = `, ...handler]
+    }
   }
 
   return [
@@ -65,6 +76,18 @@ export function genSetEvent(
       ...options.map((option): CodeFragment[] => [`${option}: true`]),
     )
   }
+
+  function isSameDelegateEvent(op: OperationNode) {
+    if (
+      op.type === IRNodeTypes.SET_EVENT &&
+      op !== oper &&
+      op.delegate &&
+      op.element === oper.element &&
+      op.key.content === key.content
+    ) {
+      return true
+    }
+  }
 }
 
 export function genSetDynamicEvents(