|
|
@@ -3,6 +3,7 @@ import {
|
|
|
defineComponent,
|
|
|
h,
|
|
|
nextTick,
|
|
|
+ onScopeDispose,
|
|
|
ref,
|
|
|
watch,
|
|
|
watchEffect,
|
|
|
@@ -10,6 +11,14 @@ import {
|
|
|
} from 'vue'
|
|
|
import { type SSRContext, renderToString } from '../src'
|
|
|
|
|
|
+const gc = () =>
|
|
|
+ new Promise<void>(resolve => {
|
|
|
+ setTimeout(() => {
|
|
|
+ global.gc!()
|
|
|
+ resolve()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
describe('ssr: watch', () => {
|
|
|
// #6013
|
|
|
test('should work w/ flush:sync', async () => {
|
|
|
@@ -284,3 +293,62 @@ describe('ssr: watchEffect', () => {
|
|
|
expect(msg).toBe('unchanged')
|
|
|
})
|
|
|
})
|
|
|
+
|
|
|
+describe.skipIf(!global.gc)('ssr: watch gc', () => {
|
|
|
+ test('should not retain apps when a watcher stop handle is registered with onScopeDispose after async context restore', async () => {
|
|
|
+ const weakRefs: { deref(): unknown | undefined }[] = []
|
|
|
+
|
|
|
+ const ComponentA = defineComponent({
|
|
|
+ async setup() {
|
|
|
+ let __temp: any, __restore: any
|
|
|
+ ;[__temp, __restore] = withAsyncContext(() => Promise.resolve(false))
|
|
|
+ const enabled = await __temp
|
|
|
+ __restore()
|
|
|
+
|
|
|
+ const el = ref(null)
|
|
|
+ const stop = watch(
|
|
|
+ () => el.value,
|
|
|
+ () => {},
|
|
|
+ { immediate: true },
|
|
|
+ )
|
|
|
+ onScopeDispose(stop)
|
|
|
+
|
|
|
+ return () => h('div', { ref: el }, `Component A ${enabled}`)
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const ComponentB = defineComponent({
|
|
|
+ async setup() {
|
|
|
+ let __temp: any, __restore: any
|
|
|
+ ;[__temp, __restore] = withAsyncContext(() => Promise.resolve(false))
|
|
|
+ const enabled = await __temp
|
|
|
+ __restore()
|
|
|
+
|
|
|
+ return () => h('div', `Component B ${enabled}`)
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ async function renderOnce() {
|
|
|
+ const app = createSSRApp({
|
|
|
+ render: () => h('div', [h(ComponentA), h(ComponentB)]),
|
|
|
+ })
|
|
|
+ // @ts-expect-error ES2021 API
|
|
|
+ weakRefs.push(new WeakRef(app))
|
|
|
+
|
|
|
+ const html = await renderToString(app)
|
|
|
+
|
|
|
+ expect(html).toContain('Component A false')
|
|
|
+ expect(html).toContain('Component B false')
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < 10; i++) {
|
|
|
+ await renderOnce()
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < 5; i++) {
|
|
|
+ await gc()
|
|
|
+ }
|
|
|
+
|
|
|
+ expect(weakRefs.filter(ref => ref.deref()).length).toBe(0)
|
|
|
+ })
|
|
|
+})
|