]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-dom): fix v-on same computed handler on multiple elements
authorEvan You <yyx990803@gmail.com>
Mon, 3 Aug 2020 21:55:22 +0000 (17:55 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 3 Aug 2020 21:55:22 +0000 (17:55 -0400)
fix #1747

packages/runtime-dom/__tests__/patchEvents.spec.ts
packages/runtime-dom/src/modules/events.ts

index f926cc690098c1117e4e15d297acfaeadc500e69..a9145f40d9a2da36a704d91262d089b6dcae8002 100644 (file)
@@ -119,4 +119,38 @@ describe(`runtime-dom: events patching`, () => {
     expect(fn1).toHaveBeenCalledTimes(1)
     expect(fn2).toHaveBeenCalledTimes(0)
   })
+
+  // #1747
+  it('should handle same computed handler function being bound on multiple targets', async () => {
+    const el1 = document.createElement('div')
+    const el2 = document.createElement('div')
+
+    const event = new Event('click')
+    const prevFn = jest.fn()
+    const nextFn = jest.fn()
+
+    patchProp(el1, 'onClick', null, prevFn)
+    patchProp(el2, 'onClick', null, prevFn)
+
+    el1.dispatchEvent(event)
+    el2.dispatchEvent(event)
+    await timeout()
+    expect(prevFn).toHaveBeenCalledTimes(2)
+    expect(nextFn).toHaveBeenCalledTimes(0)
+
+    patchProp(el1, 'onClick', prevFn, nextFn)
+    patchProp(el2, 'onClick', prevFn, nextFn)
+
+    el1.dispatchEvent(event)
+    el2.dispatchEvent(event)
+    await timeout()
+    expect(prevFn).toHaveBeenCalledTimes(2)
+    expect(nextFn).toHaveBeenCalledTimes(2)
+
+    el1.dispatchEvent(event)
+    el2.dispatchEvent(event)
+    await timeout()
+    expect(prevFn).toHaveBeenCalledTimes(2)
+    expect(nextFn).toHaveBeenCalledTimes(4)
+  })
 })
index 78199c64f475d2f03059b5c28d532e31e46cb4cf..b3ebbad23dd7f5ffa5d9b3158586a09a13faedae 100644 (file)
@@ -59,25 +59,27 @@ export function removeEventListener(
 }
 
 export function patchEvent(
-  el: Element,
+  el: Element & { _vei?: Record<string, Invoker | undefined> },
   rawName: string,
   prevValue: EventValue | null,
   nextValue: EventValue | null,
   instance: ComponentInternalInstance | null = null
 ) {
-  const invoker = prevValue && prevValue.invoker
-  if (nextValue && invoker) {
+  // vei = vue event invokers
+  const invokers = el._vei || (el._vei = {})
+  const existingInvoker = invokers[rawName]
+  if (nextValue && existingInvoker) {
     // patch
     ;(prevValue as EventValue).invoker = null
-    invoker.value = nextValue
-    nextValue.invoker = invoker
+    existingInvoker.value = nextValue
   } else {
     const [name, options] = parseName(rawName)
     if (nextValue) {
-      addEventListener(el, name, createInvoker(nextValue, instance), options)
-    } else if (invoker) {
+      const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
+      addEventListener(el, name, invoker, options)
+    } else if (existingInvoker) {
       // remove
-      removeEventListener(el, name, invoker, options)
+      removeEventListener(el, name, existingInvoker, options)
     }
   }
 }
@@ -120,7 +122,6 @@ function createInvoker(
     }
   }
   invoker.value = initialValue
-  initialValue.invoker = invoker
   invoker.attached = getNow()
   return invoker
 }