From c10e40a217b89ab7e0f7f3515242d4246ecffbdd Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 17 Jul 2024 12:14:25 +0800 Subject: [PATCH] fix(hydration): fix tracking of reactive style objects in production close #11372 --- .../runtime-core/__tests__/hydration.spec.ts | 35 ++++++++++++++++++- packages/runtime-core/src/hydration.ts | 6 ++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 6ae9caf8e79..ea1d626f7c4 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -22,6 +22,7 @@ import { nextTick, onMounted, openBlock, + reactive, ref, renderSlot, useCssVars, @@ -31,7 +32,7 @@ import { withDirectives, } from '@vue/runtime-dom' import { type SSRContext, renderToString } from '@vue/server-renderer' -import { PatchFlags } from '@vue/shared' +import { PatchFlags, normalizeStyle } from '@vue/shared' import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow' import { expect } from 'vitest' @@ -1196,6 +1197,38 @@ describe('SSR hydration', () => { expect(text.nodeType).toBe(3) }) + // #11372 + test('object style value tracking in prod', async () => { + __DEV__ = false + try { + const style = reactive({ color: 'red' }) + const Comp = { + render(this: any) { + return ( + openBlock(), + createElementBlock( + 'div', + { + style: normalizeStyle(style), + }, + null, + 4 /* STYLE */, + ) + ) + }, + } + const { container } = mountWithHydration( + `
`, + () => h(Comp), + ) + style.color = 'green' + await nextTick() + expect(container.innerHTML).toBe(`
`) + } finally { + __DEV__ = true + } + }) + test('app.unmount()', async () => { const container = document.createElement('DIV') container.innerHTML = '' diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 20ff37cdf1c..fe267f4fef6 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -39,6 +39,7 @@ import { } from './components/Suspense' import type { TeleportImpl, TeleportVNode } from './components/Teleport' import { isAsyncWrapper } from './apiAsyncComponent' +import { isReactive } from '@vue/reactivity' export type RootHydrateFunction = ( vnode: VNode, @@ -487,6 +488,11 @@ export function createHydrationFunctions( undefined, parentComponent, ) + } else if (patchFlag & PatchFlags.STYLE && isReactive(props.style)) { + // #11372: object style values are iterated during patch instead of + // render/normalization phase, but style patch is skipped during + // hydration, so we need to force iterate the object to track deps + for (const key in props.style) props.style[key] } }