From: Evan You Date: Mon, 10 Feb 2025 08:17:25 +0000 (+0800) Subject: perf(vapor): generate simpler evenet delegation code when possible X-Git-Tag: v3.6.0-alpha.1~16^2~71 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=263318db461e9db90fe8d4a2e1f08167eaf588d0;p=thirdparty%2Fvuejs%2Fcore.git perf(vapor): generate simpler evenet delegation code when possible --- diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index c3fa9f3b4b..c8c9d254d6 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -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("", 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) 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 9500b1a3eb..a26a0f5f01 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -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("
", 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)) 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 6310362601..cb1a05d2a4 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap @@ -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("
", 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("") const t1 = _template("
") const t2 = _template("
") @@ -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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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("
", 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 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts index b726895edd..1ce2169366 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts @@ -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(`
`) 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( + `
`, + ) + 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"]))', + ) + }) }) diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index a8294bd3df..cfb47b6118 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/packages/compiler-vapor/src/generators/event.ts @@ -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(