]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-dom): cache event handlers by key/modifiers (#9851)
authorThorsten Lünborg <t.luenborg@googlemail.com>
Sat, 16 Dec 2023 13:54:37 +0000 (14:54 +0100)
committerGitHub <noreply@github.com>
Sat, 16 Dec 2023 13:54:37 +0000 (21:54 +0800)
close #9849

packages/runtime-dom/__tests__/directives/vOn.spec.ts
packages/runtime-dom/src/directives/vOn.ts

index 2a4b02478f562f6e4e31740cfb03e73e82ba02e8..33ffec637df22ab6a8cef6317c72bbdf86badb55 100644 (file)
@@ -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)
+  })
 })
index 8054efb9ea551ae28ce82573fee0b0d762081fe1..823c3fb4c412480bbac4af4a2fde2d2d9bdb1b27 100644 (file)
@@ -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<string, string | string[]> = {
  * @private
  */
 export const withKeys = <T extends (event: KeyboardEvent) => any>(
-  fn: T & { _withKeys?: T },
+  fn: T & { _withKeys?: { [k: string]: T } },
   modifiers: string[]
 ) => {
   let globalKeyCodes: LegacyConfig['keyCodes']
@@ -88,9 +90,12 @@ export const withKeys = <T extends (event: KeyboardEvent) => 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
       }