]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf: should not trigger child update if changed prop is declared emit listener
authorEvan You <yyx990803@gmail.com>
Mon, 14 Sep 2020 22:58:30 +0000 (18:58 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 14 Sep 2020 22:58:30 +0000 (18:58 -0400)
close #2072

packages/runtime-core/__tests__/rendererComponent.spec.ts
packages/runtime-core/src/componentRenderUtils.ts

index 101605be8ed518b4955b06201d008fb5cb47afef..243e4cbd0bea95eebe1ae6abc71c829640634243 100644 (file)
@@ -81,4 +81,27 @@ describe('renderer: component', () => {
     render(h(Comp2), root)
     expect(serializeInner(root)).toBe('<span>foo</span>')
   })
+
+  // #2072
+  it('should not update Component if only changed props are declared emit listeners', () => {
+    const Comp1 = {
+      emits: ['foo'],
+      updated: jest.fn(),
+      render: () => null
+    }
+    const root = nodeOps.createElement('div')
+    render(
+      h(Comp1, {
+        onFoo: () => {}
+      }),
+      root
+    )
+    render(
+      h(Comp1, {
+        onFoo: () => {}
+      }),
+      root
+    )
+    expect(Comp1.updated).not.toHaveBeenCalled()
+  })
 })
index fe1a73fb21e6ced9f4257904823da96994f249c9..4a4b363f7b80e20c898943117c7525a2058f5b50 100644 (file)
@@ -18,6 +18,7 @@ import { PatchFlags, ShapeFlags, isOn, isModelListener } from '@vue/shared'
 import { warn } from './warning'
 import { isHmrUpdating } from './hmr'
 import { NormalizedProps } from './componentProps'
+import { isEmitListener } from './componentEmits'
 
 // mark the current rendering instance for asset resolution (e.g.
 // resolveComponent, resolveDirective) during render
@@ -290,8 +291,9 @@ export function shouldUpdateComponent(
   nextVNode: VNode,
   optimized?: boolean
 ): boolean {
-  const { props: prevProps, children: prevChildren } = prevVNode
+  const { props: prevProps, children: prevChildren, component } = prevVNode
   const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
+  const emits = component!.emitsOptions
 
   // Parent component's render function was hot-updated. Since this may have
   // caused the child component's slots content to have changed, we need to
@@ -316,12 +318,15 @@ export function shouldUpdateComponent(
         return !!nextProps
       }
       // presence of this flag indicates props are always non-null
-      return hasPropsChanged(prevProps, nextProps!)
+      return hasPropsChanged(prevProps, nextProps!, emits)
     } else if (patchFlag & PatchFlags.PROPS) {
       const dynamicProps = nextVNode.dynamicProps!
       for (let i = 0; i < dynamicProps.length; i++) {
         const key = dynamicProps[i]
-        if (nextProps![key] !== prevProps![key]) {
+        if (
+          nextProps![key] !== prevProps![key] &&
+          !isEmitListener(emits, key)
+        ) {
           return true
         }
       }
@@ -343,20 +348,27 @@ export function shouldUpdateComponent(
     if (!nextProps) {
       return true
     }
-    return hasPropsChanged(prevProps, nextProps)
+    return hasPropsChanged(prevProps, nextProps, emits)
   }
 
   return false
 }
 
-function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
+function hasPropsChanged(
+  prevProps: Data,
+  nextProps: Data,
+  emitsOptions: ComponentInternalInstance['emitsOptions']
+): boolean {
   const nextKeys = Object.keys(nextProps)
   if (nextKeys.length !== Object.keys(prevProps).length) {
     return true
   }
   for (let i = 0; i < nextKeys.length; i++) {
     const key = nextKeys[i]
-    if (nextProps[key] !== prevProps[key]) {
+    if (
+      nextProps[key] !== prevProps[key] &&
+      !isEmitListener(emitsOptions, key)
+    ) {
       return true
     }
   }