@@ -130,6 +130,35 @@ describe('ssr: watch', () => {
130130 expect ( msg ) . toBe ( 'start' )
131131 } )
132132
133+ // #15113
134+ test ( 'options-api watchers should not be created for async setup components' , async ( ) => {
135+ const open = ref ( true )
136+ const spy = vi . fn ( )
137+
138+ const App = defineComponent ( {
139+ setup : ( ) => Promise . resolve ( { } ) ,
140+ computed : {
141+ isOpen ( ) {
142+ return open . value
143+ } ,
144+ } ,
145+ watch : {
146+ isOpen : spy ,
147+ } ,
148+ render ( ) {
149+ return h ( 'div' , String ( this . isOpen ) )
150+ } ,
151+ } )
152+
153+ const html = await renderToString ( createSSRApp ( App ) )
154+ expect ( html ) . toBe ( '<div>true</div>' )
155+
156+ // mutating the state after the request is done should not trigger the watcher
157+ open . value = false
158+ await nextTick ( )
159+ expect ( spy ) . not . toHaveBeenCalled ( )
160+ } )
161+
133162 test ( 'should not run non-immediate watchers registered after async context restore' , async ( ) => {
134163 const text = ref ( 'start' )
135164 let beforeAwaitTriggered = false
@@ -351,4 +380,44 @@ describe.skipIf(!global.gc)('ssr: watch gc', () => {
351380
352381 expect ( weakRefs . filter ( ref => ref . deref ( ) ) . length ) . toBe ( 0 )
353382 } )
383+
384+ // #15113
385+ test ( 'options-api should not retain apps with async setup + watcher on global state' , async ( ) => {
386+ const weakRefs : { deref ( ) : unknown | undefined } [ ] = [ ]
387+
388+ const open = ref ( true )
389+
390+ const App = defineComponent ( {
391+ setup : ( ) => Promise . resolve ( { } ) ,
392+ computed : {
393+ isOpen ( ) {
394+ return open . value
395+ } ,
396+ } ,
397+ watch : {
398+ isOpen ( ) { } ,
399+ } ,
400+ render ( ) {
401+ return h ( 'div' , String ( this . isOpen ) )
402+ } ,
403+ } )
404+
405+ async function renderOnce ( ) {
406+ const app = createSSRApp ( App )
407+ // @ts -expect-error ES2021 API
408+ weakRefs . push ( new WeakRef ( app ) )
409+ const html = await renderToString ( app )
410+ expect ( html ) . toBe ( '<div>true</div>' )
411+ }
412+
413+ for ( let i = 0 ; i < 10 ; i ++ ) {
414+ await renderOnce ( )
415+ }
416+
417+ for ( let i = 0 ; i < 5 ; i ++ ) {
418+ await gc ( )
419+ }
420+
421+ expect ( weakRefs . filter ( ref => ref . deref ( ) ) . length ) . toBe ( 0 )
422+ } )
354423} )
0 commit comments