@@ -17,7 +17,10 @@ import {
1717 renderSlot ,
1818 onErrorCaptured ,
1919 onServerPrefetch ,
20- getCurrentInstance
20+ getCurrentInstance ,
21+ reactive ,
22+ computed ,
23+ createSSRApp
2124} from 'vue'
2225import { escapeHtml } from '@vue/shared'
2326import { renderToString } from '../src/renderToString'
@@ -1140,5 +1143,47 @@ function testRender(type: string, render: typeof renderToString) {
11401143 expect ( renderError ) . toBe ( null )
11411144 expect ( ( capturedError as unknown as Error ) . message ) . toBe ( 'An error' )
11421145 } )
1146+
1147+ test ( 'computed reactivity during SSR with onServerPrefetch' , async ( ) => {
1148+ const store = {
1149+ // initial state could be hydrated
1150+ state : reactive ( { items : null as null | string [ ] } ) ,
1151+
1152+ // pretend to fetch some data from an api
1153+ async fetchData ( ) {
1154+ this . state . items = [ 'hello' , 'world' ]
1155+ }
1156+ }
1157+
1158+ const getterSpy = vi . fn ( )
1159+
1160+ const App = defineComponent ( ( ) => {
1161+ const msg = computed ( ( ) => {
1162+ getterSpy ( )
1163+ return store . state . items ?. join ( ' ' )
1164+ } )
1165+
1166+ // If msg value is falsy then we are either in ssr context or on the client
1167+ // and the initial state was not modified/hydrated.
1168+ // In both cases we need to fetch data.
1169+ onServerPrefetch ( ( ) => store . fetchData ( ) )
1170+
1171+ // simulate the read from a composable (e.g. filtering a list of results)
1172+ msg . value
1173+
1174+ return ( ) => h ( 'div' , null , msg . value )
1175+ } )
1176+
1177+ const app = createSSRApp ( App )
1178+
1179+ // in real world serve this html and append store state for hydration on client
1180+ const html = await renderToString ( app )
1181+
1182+ expect ( html ) . toMatch ( 'hello world' )
1183+
1184+ // should only be called twice since access should be cached
1185+ // during the render phase
1186+ expect ( getterSpy ) . toHaveBeenCalledTimes ( 2 )
1187+ } )
11431188 } )
11441189}
0 commit comments