]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr/watch) flush: sync watchers should work in ssr (#6139)
authorwebfansplz <308241863@qq.com>
Wed, 26 Oct 2022 10:30:15 +0000 (18:30 +0800)
committerGitHub <noreply@github.com>
Wed, 26 Oct 2022 10:30:15 +0000 (06:30 -0400)
fix #6013

packages/runtime-core/src/apiWatch.ts
packages/server-renderer/__tests__/ssrWatch.spec.ts [new file with mode: 0644]
packages/server-renderer/src/render.ts
packages/server-renderer/src/renderToStream.ts
packages/server-renderer/src/renderToString.ts

index 19026cf645dff3d856db57c602ed0b25aece9acd..180843cf745596916b0f37faaf243a7b6a3e8790 100644 (file)
@@ -40,6 +40,8 @@ import { warn } from './warning'
 import { DeprecationTypes } from './compat/compatConfig'
 import { checkCompatEnabled, isCompatEnabled } from './compat/compatConfig'
 import { ObjectWatchOptionItem } from './componentOptions'
+import { useSSRContext } from '@vue/runtime-core'
+import { SSRContext } from '@vue/server-renderer'
 
 export type WatchEffect = (onCleanup: OnCleanup) => void
 
@@ -280,7 +282,8 @@ function doWatch(
   }
 
   // in SSR there is no need to setup an actual effect, and it should be noop
-  // unless it's eager
+  // unless it's eager or sync flush
+  let ssrCleanup: (() => void)[] | undefined
   if (__SSR__ && isInSSRComponentSetup) {
     // we will also not call the invalidate callback (+ runner is not set up)
     onCleanup = NOOP
@@ -293,7 +296,12 @@ function doWatch(
         onCleanup
       ])
     }
-    return NOOP
+    if (flush === 'sync') {
+      const ctx = useSSRContext() as SSRContext
+      ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = [])
+    } else {
+      return NOOP
+    }
   }
 
   let oldValue: any = isMultiSource
@@ -378,12 +386,15 @@ function doWatch(
     effect.run()
   }
 
-  return () => {
+  const unwatch = () => {
     effect.stop()
     if (instance && instance.scope) {
       remove(instance.scope.effects!, effect)
     }
   }
+
+  if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
+  return unwatch
 }
 
 // this.$watch
diff --git a/packages/server-renderer/__tests__/ssrWatch.spec.ts b/packages/server-renderer/__tests__/ssrWatch.spec.ts
new file mode 100644 (file)
index 0000000..df08533
--- /dev/null
@@ -0,0 +1,30 @@
+import { createSSRApp, defineComponent, h, watch, ref } from 'vue'
+import { SSRContext, renderToString } from '../src'
+
+describe('ssr: watch', () => {
+  // #6013
+  test('should work w/ flush:sync', async () => {
+    const App = defineComponent(() => {
+      const count = ref(0)
+      let msg = ''
+      watch(
+        count,
+        () => {
+          msg = 'hello world'
+        },
+        { flush: 'sync' }
+      )
+      count.value = 1
+      expect(msg).toBe('hello world')
+      return () => h('div', null, msg)
+    })
+
+    const app = createSSRApp(App)
+    const ctx: SSRContext = {}
+    const html = await renderToString(app, ctx)
+
+    expect(ctx.__watcherHandles!.length).toBe(1)
+
+    expect(html).toMatch('hello world')
+  })
+})
index 65123ac0cd3af9fccc6a08858583a05af9854be5..a1f327b4320931502304ad4903abf25e1df1a74b 100644 (file)
@@ -45,7 +45,14 @@ export type Props = Record<string, unknown>
 export type SSRContext = {
   [key: string]: any
   teleports?: Record<string, string>
+  /**
+   * @internal
+   */
   __teleportBuffers?: Record<string, SSRBuffer>
+  /**
+   * @internal
+   */
+  __watcherHandles?: (() => void)[]
 }
 
 // Each component has a buffer array.
index 79484f9cffc292405441734f7d6d0484119cd21e..bdcdb0a026e1451d98db2d4ea8b9be241dccc284 100644 (file)
@@ -76,6 +76,13 @@ export function renderToSimpleStream<T extends SimpleReadable>(
   Promise.resolve(renderComponentVNode(vnode))
     .then(buffer => unrollBuffer(buffer, stream))
     .then(() => resolveTeleports(context))
+    .then(() => {
+      if (context.__watcherHandles) {
+        for (const unwatch of context.__watcherHandles) {
+          unwatch()
+        }
+      }
+    })
     .then(() => stream.push(null))
     .catch(error => {
       stream.destroy(error)
index 3db4561c74ee44e3996f8232d3a4eed73a020811..2fdc39f681676cb4282c0c15ef4e45cb4b01021c 100644 (file)
@@ -67,6 +67,12 @@ export async function renderToString(
 
   await resolveTeleports(context)
 
+  if (context.__watcherHandles) {
+    for (const unwatch of context.__watcherHandles) {
+      unwatch()
+    }
+  }
+
   return result
 }