11x5 精读Vue官方文档 - CookBook - 避免内存泄漏

821 阅读2分钟

精读 Vue 官方文档系列 🎉


简介

SPA 应用相比与 MPA 更容易产生“内存泄漏”。因为用户在使用 SPA 应用时是不需要刷新浏览器的。此时,就需要应用自身或者是浏览器来清理无用的代码,确保垃圾回收符合预期。

出现的原因

当你在一个 Vue 应用中使用到了非 Vue 的库、插件、组件、事件行为处理、DOM 渲染等,那么就非常有可能产生内存泄漏。

触发的场景

  • 组件被销毁时,没有清理相关的依赖。
  • 路由进入离开时,没有清理相关的依赖。
  • keepAlive 切换时,没有清理相关的依赖。

产生的影响

应用的内存占有不断提高,影响应用的使用性能,带来可能的安全隐患。

实际的例子

下面是一个具体的实例,切换显示/隐藏按钮时,每次都会创建一个新的应用实例。

模板部分:

<button v-if="show" @click="show=false">hide</button>
<button v-if="!show" @click="handleShow">show</button>
<div v-if="show" id="app"></div>

组件逻辑部分:

{
  data() {
    return {
      show: true,
    };
  },
  methods: {
    initApp() {
      new App({ el: "#app" });
    },
    handleShow() {
      this.show = true;
      this.$nextTick(() => {
        this.initApp();
      });
    },
  },
  mounted() {
    this.initApp();
  },
}

对于这种场景,就会产生内存泄漏,虽然每次隐藏时 Vue 会清除 #app 里的 DOM 元素,但是构造函数产生的实例在内部的内存引用依然存在,且每次执行 handleShow 方法都会重复创建实例,多次反复,内存的占有就一直提高,并且得不到释放。

解决的方法很简单,那就是定义一个变量来接收每次产生的实例,然后再组件的 beforeDestroy 钩子中执行这个实例的销毁方法。

{
  beforeDestroy(){
    // this.app = new App({el:'#app'}) in methods handleShow。
    this.app.destroyed();
    this.app = null;
  }
}

举一反三,在 Vue 官方文档的 “示例 - 内嵌组件” 一章中,我们提到了如何在 Vue 中整合基于 Jquery 生态的传统插件或组件。对于类似的场景,必须要引起我们对内存泄漏的注意。

结合上述实例,一些典型的会导致内存泄漏的场景:

  • 使用第三方库没有正确调用销毁函数。
  • 如果你将一个局部作用域的数据(例如组件)存储到全局中(例如 window),就会产生内存泄漏。典型的例如 EventBus 事件总线技术。
  • 为当前组件作用域外的 DOM 或对象绑定事件,并且在组件销毁时没有清理,例如为 window 绑定的事件、定时器等。

解决的方案

核心秘诀永远是记得保持警惕,特别是使用了第三方具有 DOM 操作的库。

  1. 在组件 beforeDestroy 钩子中进行清理。
  2. 对于 keep-alive 可以在 deactivated 中进行清理。

如何检查?

可以借助浏览器的 Memory - Heap snapshot 工具来分析。