mirror of https://github.com/vuejs/core.git
fix(ssr/watch) flush: sync watchers should work in ssr (#6139)
fix #6013
This commit is contained in:
parent
32b51249bf
commit
0e0976168f
|
@ -40,6 +40,8 @@ import { warn } from './warning'
|
||||||
import { DeprecationTypes } from './compat/compatConfig'
|
import { DeprecationTypes } from './compat/compatConfig'
|
||||||
import { checkCompatEnabled, isCompatEnabled } from './compat/compatConfig'
|
import { checkCompatEnabled, isCompatEnabled } from './compat/compatConfig'
|
||||||
import { ObjectWatchOptionItem } from './componentOptions'
|
import { ObjectWatchOptionItem } from './componentOptions'
|
||||||
|
import { useSSRContext } from '@vue/runtime-core'
|
||||||
|
import { SSRContext } from '@vue/server-renderer'
|
||||||
|
|
||||||
export type WatchEffect = (onCleanup: OnCleanup) => void
|
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
|
// 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) {
|
if (__SSR__ && isInSSRComponentSetup) {
|
||||||
// we will also not call the invalidate callback (+ runner is not set up)
|
// we will also not call the invalidate callback (+ runner is not set up)
|
||||||
onCleanup = NOOP
|
onCleanup = NOOP
|
||||||
|
@ -293,8 +296,13 @@ function doWatch(
|
||||||
onCleanup
|
onCleanup
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
if (flush === 'sync') {
|
||||||
|
const ctx = useSSRContext() as SSRContext
|
||||||
|
ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = [])
|
||||||
|
} else {
|
||||||
return NOOP
|
return NOOP
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let oldValue: any = isMultiSource
|
let oldValue: any = isMultiSource
|
||||||
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
|
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
|
||||||
|
@ -378,12 +386,15 @@ function doWatch(
|
||||||
effect.run()
|
effect.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
const unwatch = () => {
|
||||||
effect.stop()
|
effect.stop()
|
||||||
if (instance && instance.scope) {
|
if (instance && instance.scope) {
|
||||||
remove(instance.scope.effects!, effect)
|
remove(instance.scope.effects!, effect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
|
||||||
|
return unwatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.$watch
|
// this.$watch
|
||||||
|
|
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
|
@ -45,7 +45,14 @@ export type Props = Record<string, unknown>
|
||||||
export type SSRContext = {
|
export type SSRContext = {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
teleports?: Record<string, string>
|
teleports?: Record<string, string>
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
__teleportBuffers?: Record<string, SSRBuffer>
|
__teleportBuffers?: Record<string, SSRBuffer>
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
__watcherHandles?: (() => void)[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each component has a buffer array.
|
// Each component has a buffer array.
|
||||||
|
|
|
@ -76,6 +76,13 @@ export function renderToSimpleStream<T extends SimpleReadable>(
|
||||||
Promise.resolve(renderComponentVNode(vnode))
|
Promise.resolve(renderComponentVNode(vnode))
|
||||||
.then(buffer => unrollBuffer(buffer, stream))
|
.then(buffer => unrollBuffer(buffer, stream))
|
||||||
.then(() => resolveTeleports(context))
|
.then(() => resolveTeleports(context))
|
||||||
|
.then(() => {
|
||||||
|
if (context.__watcherHandles) {
|
||||||
|
for (const unwatch of context.__watcherHandles) {
|
||||||
|
unwatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(() => stream.push(null))
|
.then(() => stream.push(null))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
stream.destroy(error)
|
stream.destroy(error)
|
||||||
|
|
|
@ -67,6 +67,12 @@ export async function renderToString(
|
||||||
|
|
||||||
await resolveTeleports(context)
|
await resolveTeleports(context)
|
||||||
|
|
||||||
|
if (context.__watcherHandles) {
|
||||||
|
for (const unwatch of context.__watcherHandles) {
|
||||||
|
unwatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue