nextTick,
onMounted,
openBlock,
+ reactive,
ref,
renderSlot,
useCssVars,
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'
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(
+ `<div style="color: red;"></div>`,
+ () => h(Comp),
+ )
+ style.color = 'green'
+ await nextTick()
+ expect(container.innerHTML).toBe(`<div style="color: green;"></div>`)
+ } finally {
+ __DEV__ = true
+ }
+ })
+
test('app.unmount()', async () => {
const container = document.createElement('DIV')
container.innerHTML = '<button></button>'
} from './components/Suspense'
import type { TeleportImpl, TeleportVNode } from './components/Teleport'
import { isAsyncWrapper } from './apiAsyncComponent'
+import { isReactive } from '@vue/reactivity'
export type RootHydrateFunction = (
vnode: VNode<Node, Element>,
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]
}
}