فهرست منبع

fix(TransitionGroup): reset prevChildren to prevent memory leak (#13183)

close #13181
edison 11 ماه پیش
والد
کامیت
8b848cbbd2
2فایلهای تغییر یافته به همراه53 افزوده شده و 0 حذف شده
  1. 2 0
      packages/runtime-dom/src/components/TransitionGroup.ts
  2. 51 0
      packages/vue/__tests__/e2e/TransitionGroup.spec.ts

+ 2 - 0
packages/runtime-dom/src/components/TransitionGroup.ts

@@ -81,6 +81,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
           moveClass,
         )
       ) {
+        prevChildren = []
         return
       }
 
@@ -110,6 +111,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
         })
         el.addEventListener('transitionend', cb)
       })
+      prevChildren = []
     })
 
     return () => {

+ 51 - 0
packages/vue/__tests__/e2e/TransitionGroup.spec.ts

@@ -645,4 +645,55 @@ describe('e2e: TransitionGroup', () => {
     },
     E2E_TIMEOUT,
   )
+
+  test(
+    'not leaking after children unmounted',
+    async () => {
+      const client = await page().createCDPSession()
+      await page().evaluate(async () => {
+        const { createApp, ref, nextTick } = (window as any).Vue
+        const show = ref(true)
+
+        createApp({
+          components: {
+            Child: {
+              setup: () => {
+                // Big arrays kick GC earlier
+                const test = ref([...Array(3000)].map((_, i) => ({ i })))
+                // @ts-expect-error - Custom property and same lib as runtime is used
+                window.__REF__ = new WeakRef(test)
+
+                return { test }
+              },
+              template: `
+              <p>{{ test.length }}</p>
+            `,
+            },
+          },
+          template: `
+          <transition-group>
+            <Child v-if="show" />
+          </transition-group>
+        `,
+          setup() {
+            return { show }
+          },
+        }).mount('#app')
+
+        show.value = false
+        await nextTick()
+      })
+
+      const isCollected = async () =>
+        // @ts-expect-error - Custom property
+        await page().evaluate(() => window.__REF__.deref() === undefined)
+
+      while ((await isCollected()) === false) {
+        await client.send('HeapProfiler.collectGarbage')
+      }
+
+      expect(await isCollected()).toBe(true)
+    },
+    E2E_TIMEOUT,
+  )
 })