vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

Home Page:https://vuejs.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

KeepAlive + Transition + v-if 同时使用时会导致时会导致内存泄漏的2个bug

wcldyx opened this issue · comments

Vue version

3.4.21

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-futfv6?file=src%2FApp.vue

Steps to reproduce

测试的时候注意自己创建一个新项目测试,因为stackblitz上看内存占用不方便

有两个bug

第一个bug: KeepAlive + Transition + v-if 同时使用时会导致时会导致内存泄漏。

如图所示,通过点击toggle按钮用v-if 不停的切换显示隐藏组件,你会看到内存一直没有被释放。

image
动画1

单独使用KeepAlive 时在通过v-if隐藏组件时内存会自动释放。

动画2

第二个bug: 在 mode="out-in"时通过v-if切换并同时移除include里面的key时,对应的组件没有被正常卸载(没有触发onUnmounted), 内存也没有得到释放。

mode为默认值时,组件能正常卸载

动画3

mode="out-in" 时,组件没有被正常卸载

动画4

What is expected?

修复以上bug

What is actually happening?

KeepAlive + Transition + v-if 同时使用时会导致时会导致内存泄漏,同时在 Transition 的 mode="out-in"时,组件没有被正常卸载或销毁

System Info

No response

Any additional comments?

No response

以下是完整代码

<script lang="ts" setup>
import { ref, defineComponent, h, onUnmounted } from 'vue';
// import A from "./A.vue";

const show = ref(true);
const include = ref(['A']);

const A = defineComponent({
  name: 'A',
  setup() {
    const list = ref(new Array(1000000).fill({ name: 'hello' }));
    console.log('安装');
    onUnmounted(() => {
      console.log('卸载');
    });
    return () => h('h1', 'A组件' + list.value.length);
  },
});

function toggle() {
  show.value = !show.value;
  if (show.value) {
    include.value = ['A'];
  } else {
    include.value = [];
  }
}
</script>
<template>
  <div>include:{{ include }}</div>
  <button @click="toggle">toggle</button>
  <Transition mode="out-in">
    <KeepAlive :include="include">
      <A v-if="show" />
    </KeepAlive>
  </Transition>
</template>

第一个问题的复现是把 mode 和 include 都去掉吗?

  <Transition>
    <KeepAlive>
      <A v-if="show" />
    </KeepAlive>
  </Transition>

image
把 mode 和 include 都去掉后,不停切换并不存在内存泄漏

第一个问题的复现是把 mode 和 include 都去掉吗?

  <Transition>
    <KeepAlive>
      <A v-if="show" />
    </KeepAlive>
  </Transition>

image 把 mode 和 include 都去掉后,不停切换并不存在内存泄漏

两个问题都是在有include 的情况下会出现内存溢出

@wcldyx 这是因为在开发环境会在元素上定义 __vnode,公开给 vue-devtools 使用。生产环境不会内存溢出

if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
Object.defineProperty(el, '__vnode', {
value: vnode,
enumerable: false,
})
Object.defineProperty(el, '__vueParentComponent', {
value: parentComponent,
enumerable: false,
})
}

@wcldyx 这是因为在开发环境会在元素上定义 __vnode,公开给 vue-devtools 使用。生产环境不会内存溢出

if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
Object.defineProperty(el, '__vnode', {
value: vnode,
enumerable: false,
})
Object.defineProperty(el, '__vueParentComponent', {
value: parentComponent,
enumerable: false,
})
}

6啊,你是怎么定位到这个问题的

@wcldyx
image
类似的问题已经有人提了好多次了。

Leave it open because the PR for the second issue has not yet merged