]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hydration): fix tracking of reactive style objects in production
authorEvan You <evan@vuejs.org>
Wed, 17 Jul 2024 04:14:25 +0000 (12:14 +0800)
committerEvan You <evan@vuejs.org>
Wed, 17 Jul 2024 04:14:25 +0000 (12:14 +0800)
close #11372

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

index 6ae9caf8e796c1f3ab7c74149b254bf454ab3199..ea1d626f7c4ccfb41a1d20bb6c76899e3ced9753 100644 (file)
@@ -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(
+        `<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>'
index 20ff37cdf1ce0068976bdcea21571cae4be8b730..fe267f4fef61a0907982166c20a9122f09255f32 100644 (file)
@@ -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<Node, Element>,
@@ -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]
         }
       }