Vue2 中 setTimeout 导致 DOM 节点无法回收的内存泄露问题?

vue2 内存泄露问题
测试页非常简单

  1. 使用 vue create 创建了一个初始项目
  2. 把代码改造如下

    • Home.vue
    <template>
      <div class="home">
     <button @click="close">close</button>
     <button @click="show">show</button>
     <exceptionList v-if="showDialog"/>
      </div>
    </template>
    
    <script>
    export default {
      name: 'Home',
      data () {
     return {
       showDialog: false
     }
      },
      components: {
     // HelloWorld,
     exceptionList: () => import('@/components/exception')
      },
      methods: {
     close () {
       this.showDialog = false
     },
     show () {
       this.showDialog = true
     }
      }
    }
    </script>
    <style>
    button{
      width:100px;
      height:30px;
    }
    </style>
  • components/exception.vue

    <template>
    <div class="ipc-dialog">
      <div>{{ total }}</div>
      <!-- 列表展示 -->
      <div
        v-for="(item,index) in 1000"
        :key="`${item}-${index}`"
        class="item"
      >
        {{ item }}ipc-prod2.8
      </div>
    </div>
    </template>
    
    <script>
    export default {
    name: 'Exception',
    data () {
      return {
        total: 1000
      }
    },
    mounted () {
      this.timer = setTimeout(() => {
        this.total = 10000
      }, 1000)
    },
    beforeDestroy () {
      clearTimeout(this.timer)
    }
    }
    </script>
  1. 进入到 home 页面
    image.png
  2. 点击 show 按钮, 1000ms(间隔时间越大,必现概率越高) 后点击 close 按钮
    image.png
    观察 DOM节点无法被回收,并且持续泄露
    image.png

解决后 一杯奶茶奉上~

阅读 2.9k
2 个回答

你执行时机不对,定时器已经加到任务队列里面的你那样只能清除没队列的里的:

<script>
export default {
  name: 'Exception',
  data() {
    return {
      total: 1000,
      timer: null
    };
  },
  mounted() {
    this.timer = setTimeout(() => {
      this.total = 10000;
      this.timer = null; 
    }, 1000);
  },
  beforeDestroy() {
    if (this.timer) {
      
      this.total = 10000; 
      clearTimeout(this.timer);
      this.timer = null; 
    }
  }
};
</script>

或者再加一个变量:

<script>
export default {
  name: 'Exception',
  data() {
    return {
      total: 1000,
      timer: null,
      isMounted: false
    };
  },
  mounted() {
    this.isMounted = true;
    this.timer = setTimeout(() => {
      if (this.isMounted) {
        this.total = 10000;
      }
    }, 1000);
  },
  beforeDestroy() {
    this.isMounted = false;
    clearTimeout(this.timer);
  }
};
</script>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏