Skip to content

Commit c10e40a

Browse files
committedJul 17, 2024··
fix(hydration): fix tracking of reactive style objects in production
close #11372
1 parent a509e30 commit c10e40a

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed
 

‎packages/runtime-core/__tests__/hydration.spec.ts

+34-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
nextTick,
2323
onMounted,
2424
openBlock,
25+
reactive,
2526
ref,
2627
renderSlot,
2728
useCssVars,
@@ -31,7 +32,7 @@ import {
3132
withDirectives,
3233
} from '@vue/runtime-dom'
3334
import { type SSRContext, renderToString } from '@vue/server-renderer'
34-
import { PatchFlags } from '@vue/shared'
35+
import { PatchFlags, normalizeStyle } from '@vue/shared'
3536
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
3637
import { expect } from 'vitest'
3738

@@ -1196,6 +1197,38 @@ describe('SSR hydration', () => {
11961197
expect(text.nodeType).toBe(3)
11971198
})
11981199

1200+
// #11372
1201+
test('object style value tracking in prod', async () => {
1202+
__DEV__ = false
1203+
try {
1204+
const style = reactive({ color: 'red' })
1205+
const Comp = {
1206+
render(this: any) {
1207+
return (
1208+
openBlock(),
1209+
createElementBlock(
1210+
'div',
1211+
{
1212+
style: normalizeStyle(style),
1213+
},
1214+
null,
1215+
4 /* STYLE */,
1216+
)
1217+
)
1218+
},
1219+
}
1220+
const { container } = mountWithHydration(
1221+
`<div style="color: red;"></div>`,
1222+
() => h(Comp),
1223+
)
1224+
style.color = 'green'
1225+
await nextTick()
1226+
expect(container.innerHTML).toBe(`<div style="color: green;"></div>`)
1227+
} finally {
1228+
__DEV__ = true
1229+
}
1230+
})
1231+
11991232
test('app.unmount()', async () => {
12001233
const container = document.createElement('DIV')
12011234
container.innerHTML = '<button></button>'

‎packages/runtime-core/src/hydration.ts

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
} from './components/Suspense'
4040
import type { TeleportImpl, TeleportVNode } from './components/Teleport'
4141
import { isAsyncWrapper } from './apiAsyncComponent'
42+
import { isReactive } from '@vue/reactivity'
4243

4344
export type RootHydrateFunction = (
4445
vnode: VNode<Node, Element>,
@@ -487,6 +488,11 @@ export function createHydrationFunctions(
487488
undefined,
488489
parentComponent,
489490
)
491+
} else if (patchFlag & PatchFlags.STYLE && isReactive(props.style)) {
492+
// #11372: object style values are iterated during patch instead of
493+
// render/normalization phase, but style patch is skipped during
494+
// hydration, so we need to force iterate the object to track deps
495+
for (const key in props.style) props.style[key]
490496
}
491497
}
492498

0 commit comments

Comments
 (0)
Please sign in to comment.