From 1c967fc44b971686d5a0e2811deb2362ec84979f Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 3 Aug 2020 17:55:22 -0400 Subject: [PATCH] fix(runtime-dom): fix v-on same computed handler on multiple elements fix #1747 --- .../runtime-dom/__tests__/patchEvents.spec.ts | 34 +++++++++++++++++++ packages/runtime-dom/src/modules/events.ts | 19 ++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/runtime-dom/__tests__/patchEvents.spec.ts b/packages/runtime-dom/__tests__/patchEvents.spec.ts index f926cc69009..a9145f40d9a 100644 --- a/packages/runtime-dom/__tests__/patchEvents.spec.ts +++ b/packages/runtime-dom/__tests__/patchEvents.spec.ts @@ -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) + }) }) diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index 78199c64f47..b3ebbad23dd 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -59,25 +59,27 @@ export function removeEventListener( } export function patchEvent( - el: Element, + el: Element & { _vei?: Record }, 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 }