This commit is contained in:
edison 2025-07-01 17:41:03 +08:00 committed by GitHub
commit c030f18c24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 3 deletions

View File

@ -6,6 +6,7 @@ import {
inject,
nextTick,
nodeOps,
onMounted,
provide,
ref,
render,
@ -474,4 +475,55 @@ describe('renderer: component', () => {
`Property '$attrs' was accessed via 'this'. Avoid using 'this' in templates.`,
).toHaveBeenWarned()
})
test('should not update child component if style is not changed', async () => {
const text = ref(0)
const spy = vi.fn()
const ClientOnly = {
setup(_: any, { slots }: SetupContext) {
const mounted = ref(false)
onMounted(() => {
mounted.value = true
})
return () => {
if (mounted.value) {
return slots.default!()
}
}
},
}
const App = {
render() {
return h(ClientOnly, null, {
default: () => [
h('span', null, [text.value]),
h(Comp, { style: { width: '100%' } }),
],
})
},
}
const Comp = {
render(this: any) {
spy()
return null
},
}
const root = nodeOps.createElement('div')
render(h(App), root)
expect(serializeInner(root)).toBe(`<!---->`)
await nextTick()
expect(serializeInner(root)).toBe(`<span>0</span><!---->`)
expect(spy).toHaveBeenCalledTimes(1)
text.value++
await nextTick()
expect(serializeInner(root)).toBe(`<span>1</span><!---->`)
// expect Comp to not be re-rendered
expect(spy).toHaveBeenCalledTimes(1)
})
})

View File

@ -15,7 +15,14 @@ import {
normalizeVNode,
} from './vnode'
import { ErrorCodes, handleError } from './errorHandling'
import { PatchFlags, ShapeFlags, isModelListener, isOn } from '@vue/shared'
import {
PatchFlags,
ShapeFlags,
isModelListener,
isObject,
isOn,
looseEqual,
} from '@vue/shared'
import { warn } from './warning'
import { isHmrUpdating } from './hmr'
import type { NormalizedProps } from './componentProps'
@ -399,7 +406,7 @@ export function shouldUpdateComponent(
for (let i = 0; i < dynamicProps.length; i++) {
const key = dynamicProps[i]
if (
nextProps![key] !== prevProps![key] &&
hasPropValueChanged(nextProps!, prevProps!, key) &&
!isEmitListener(emits, key)
) {
return true
@ -441,7 +448,7 @@ function hasPropsChanged(
for (let i = 0; i < nextKeys.length; i++) {
const key = nextKeys[i]
if (
nextProps[key] !== prevProps[key] &&
hasPropValueChanged(nextProps, prevProps, key) &&
!isEmitListener(emitsOptions, key)
) {
return true
@ -450,6 +457,19 @@ function hasPropsChanged(
return false
}
function hasPropValueChanged(
nextProps: Data,
prevProps: Data,
key: string,
): boolean {
const nextProp = nextProps[key]
const prevProp = prevProps[key]
if (key === 'style' && isObject(nextProp) && isObject(prevProp)) {
return !looseEqual(nextProp, prevProp)
}
return nextProp !== prevProp
}
export function updateHOCHostEl(
{ vnode, parent }: ComponentInternalInstance,
el: typeof vnode.el, // HostNode