Explorar o código

fix(hydration): fix tracking of reactive style objects in production

close #11372
Evan You hai 1 ano
pai
achega
c10e40a217

+ 34 - 1
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(
+        `<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>'

+ 6 - 0
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<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]
         }
       }