perf: should not trigger child update if changed prop is declared emit listener
close #2072
This commit is contained in:
parent
4de5b111ee
commit
124c385baf
@ -81,4 +81,27 @@ describe('renderer: component', () => {
|
|||||||
render(h(Comp2), root)
|
render(h(Comp2), root)
|
||||||
expect(serializeInner(root)).toBe('<span>foo</span>')
|
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()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -18,6 +18,7 @@ import { PatchFlags, ShapeFlags, isOn, isModelListener } from '@vue/shared'
|
|||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { isHmrUpdating } from './hmr'
|
import { isHmrUpdating } from './hmr'
|
||||||
import { NormalizedProps } from './componentProps'
|
import { NormalizedProps } from './componentProps'
|
||||||
|
import { isEmitListener } from './componentEmits'
|
||||||
|
|
||||||
// mark the current rendering instance for asset resolution (e.g.
|
// mark the current rendering instance for asset resolution (e.g.
|
||||||
// resolveComponent, resolveDirective) during render
|
// resolveComponent, resolveDirective) during render
|
||||||
@ -290,8 +291,9 @@ export function shouldUpdateComponent(
|
|||||||
nextVNode: VNode,
|
nextVNode: VNode,
|
||||||
optimized?: boolean
|
optimized?: boolean
|
||||||
): boolean {
|
): boolean {
|
||||||
const { props: prevProps, children: prevChildren } = prevVNode
|
const { props: prevProps, children: prevChildren, component } = prevVNode
|
||||||
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
|
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
|
||||||
|
const emits = component!.emitsOptions
|
||||||
|
|
||||||
// Parent component's render function was hot-updated. Since this may have
|
// 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
|
// caused the child component's slots content to have changed, we need to
|
||||||
@ -316,12 +318,15 @@ export function shouldUpdateComponent(
|
|||||||
return !!nextProps
|
return !!nextProps
|
||||||
}
|
}
|
||||||
// presence of this flag indicates props are always non-null
|
// presence of this flag indicates props are always non-null
|
||||||
return hasPropsChanged(prevProps, nextProps!)
|
return hasPropsChanged(prevProps, nextProps!, emits)
|
||||||
} else if (patchFlag & PatchFlags.PROPS) {
|
} else if (patchFlag & PatchFlags.PROPS) {
|
||||||
const dynamicProps = nextVNode.dynamicProps!
|
const dynamicProps = nextVNode.dynamicProps!
|
||||||
for (let i = 0; i < dynamicProps.length; i++) {
|
for (let i = 0; i < dynamicProps.length; i++) {
|
||||||
const key = dynamicProps[i]
|
const key = dynamicProps[i]
|
||||||
if (nextProps![key] !== prevProps![key]) {
|
if (
|
||||||
|
nextProps![key] !== prevProps![key] &&
|
||||||
|
!isEmitListener(emits, key)
|
||||||
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,20 +348,27 @@ export function shouldUpdateComponent(
|
|||||||
if (!nextProps) {
|
if (!nextProps) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return hasPropsChanged(prevProps, nextProps)
|
return hasPropsChanged(prevProps, nextProps, emits)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
|
function hasPropsChanged(
|
||||||
|
prevProps: Data,
|
||||||
|
nextProps: Data,
|
||||||
|
emitsOptions: ComponentInternalInstance['emitsOptions']
|
||||||
|
): boolean {
|
||||||
const nextKeys = Object.keys(nextProps)
|
const nextKeys = Object.keys(nextProps)
|
||||||
if (nextKeys.length !== Object.keys(prevProps).length) {
|
if (nextKeys.length !== Object.keys(prevProps).length) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for (let i = 0; i < nextKeys.length; i++) {
|
for (let i = 0; i < nextKeys.length; i++) {
|
||||||
const key = nextKeys[i]
|
const key = nextKeys[i]
|
||||||
if (nextProps[key] !== prevProps[key]) {
|
if (
|
||||||
|
nextProps[key] !== prevProps[key] &&
|
||||||
|
!isEmitListener(emitsOptions, key)
|
||||||
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user