diff --git a/packages/runtime-dom/__tests__/directives/vOn.spec.ts b/packages/runtime-dom/__tests__/directives/vOn.spec.ts index 2a4b02478f5..33ffec637df 100644 --- a/packages/runtime-dom/__tests__/directives/vOn.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vOn.spec.ts @@ -135,4 +135,32 @@ describe('runtime-dom: v-on directive', () => { handler(event, 'value', true) expect(fn).toBeCalledWith(event, 'value', true) }) + + it('withKeys cache wrapped listener separately for different modifiers', () => { + const el1 = document.createElement('button') + const el2 = document.createElement('button') + const fn = vi.fn() + const handler1 = withKeys(fn, ['a']) + const handler2 = withKeys(fn, ['b']) + expect(handler1 === handler2).toBe(false) + patchEvent(el1, 'onKeyup', null, handler1, null) + patchEvent(el2, 'onKeyup', null, handler2, null) + triggerEvent(el1, 'keyup', e => (e.key = 'a')) + triggerEvent(el2, 'keyup', e => (e.key = 'b')) + expect(fn).toBeCalledTimes(2) + }) + + it('withModifiers cache wrapped listener separately for different modifiers', () => { + const el1 = document.createElement('button') + const el2 = document.createElement('button') + const fn = vi.fn() + const handler1 = withModifiers(fn, ['ctrl']) + const handler2 = withModifiers(fn, ['shift']) + expect(handler1 === handler2).toBe(false) + patchEvent(el1, 'onClick', null, handler1, null) + patchEvent(el2, 'onClick', null, handler2, null) + triggerEvent(el1, 'click', e => (e.ctrlKey = true)) + triggerEvent(el2, 'click', e => (e.shiftKey = true)) + expect(fn).toBeCalledTimes(2) + }) }) diff --git a/packages/runtime-dom/src/directives/vOn.ts b/packages/runtime-dom/src/directives/vOn.ts index 8054efb9ea5..823c3fb4c41 100644 --- a/packages/runtime-dom/src/directives/vOn.ts +++ b/packages/runtime-dom/src/directives/vOn.ts @@ -35,12 +35,14 @@ const modifierGuards: Record< export const withModifiers = < T extends (event: Event, ...args: unknown[]) => any >( - fn: T & { _withMods?: T }, + fn: T & { _withMods?: { [key: string]: T } }, modifiers: string[] ) => { + const cache = fn._withMods || (fn._withMods = {}) + const cacheKey = modifiers.join('.') return ( - fn._withMods || - (fn._withMods = ((event, ...args) => { + cache[cacheKey] || + (cache[cacheKey] = ((event, ...args) => { for (let i = 0; i < modifiers.length; i++) { const guard = modifierGuards[modifiers[i]] if (guard && guard(event, modifiers)) return @@ -66,7 +68,7 @@ const keyNames: Record = { * @private */ export const withKeys = any>( - fn: T & { _withKeys?: T }, + fn: T & { _withKeys?: { [k: string]: T } }, modifiers: string[] ) => { let globalKeyCodes: LegacyConfig['keyCodes'] @@ -88,9 +90,12 @@ export const withKeys = any>( } } + const cache: { [k: string]: T } = fn._withKeys || (fn._withKeys = {}) + const cacheKey = modifiers.join('.') + return ( - fn._withKeys || - (fn._withKeys = (event => { + cache[cacheKey] || + (cache[cacheKey] = (event => { if (!('key' in event)) { return }