]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): improve dedupe listeners when attr fallthrough (#4912)
authoredison <daiwei521@126.com>
Mon, 15 Nov 2021 03:20:38 +0000 (11:20 +0800)
committerGitHub <noreply@github.com>
Mon, 15 Nov 2021 03:20:38 +0000 (22:20 -0500)
fix #4859

packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
packages/runtime-core/src/vnode.ts

index d358431d66b8b57ccd9b186de87ae09e6e4ca2d5..f23cdf6a27c46de5188d383eaf59ac017545f16d 100644 (file)
@@ -11,7 +11,8 @@ import {
   createBlock,
   FunctionalComponent,
   createCommentVNode,
-  Fragment
+  Fragment,
+  withModifiers
 } from '@vue/runtime-dom'
 import { PatchFlags } from '@vue/shared/src'
 
@@ -383,6 +384,45 @@ describe('attribute fallthrough', () => {
     expect(`Extraneous non-emits event listeners`).toHaveBeenWarned()
   })
 
+  it('should dedupe same listeners when $attrs is used during render', () => {
+    const click = jest.fn()
+    const count = ref(0)
+
+    function inc() {
+      count.value++
+      click()
+    }
+
+    const Parent = {
+      render() {
+        return h(Child, { onClick: inc })
+      }
+    }
+
+    const Child = defineComponent({
+      render() {
+        return h(
+          'div',
+          mergeProps(
+            {
+              onClick: withModifiers(() => {}, ['prevent', 'stop'])
+            },
+            this.$attrs
+          )
+        )
+      }
+    })
+
+    const root = document.createElement('div')
+    document.body.appendChild(root)
+    render(h(Parent), root)
+
+    const node = root.children[0] as HTMLElement
+    node.dispatchEvent(new CustomEvent('click'))
+    expect(click).toHaveBeenCalledTimes(1)
+    expect(count.value).toBe(1)
+  })
+
   it('should not warn when $attrs is used during render', () => {
     const Parent = {
       render() {
index 089147a38f7fda2d396b40732bc0ce4a339ab206..306afbcc3d24c3b782f43c5a5d1dd32560f97ea2 100644 (file)
@@ -791,7 +791,10 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
       } else if (isOn(key)) {
         const existing = ret[key]
         const incoming = toMerge[key]
-        if (existing !== incoming) {
+        if (
+          existing !== incoming &&
+          !(isArray(existing) && existing.includes(incoming))
+        ) {
           ret[key] = existing
             ? [].concat(existing as any, incoming as any)
             : incoming