Vue实现定位并解决内存泄漏

 更新时间:2023年09月11日 09:23:46   作者:Sean  
Vue.js 是一个流行且强大的 JavaScript 框架,它允许我们构建动态和交互式 Web 应用程序,本文我们将深入探讨 Vue.js 应用程序中内存泄漏的原因,并探索如何定位和修复这些问题的有效策略,希望对大家有所帮助

Vue.js 是一个流行且强大的 JavaScript 框架,它允许我们构建动态和交互式 Web 应用程序。

然而,与任何软件一样,Vue.js 应用程序有时会遇到内存泄漏,从而导致性能下降和意外行为。

今天,我们将深入探讨 Vue.js 应用程序中内存泄漏的原因,并探索如何定位和修复这些问题的有效策略。

什么是内存泄漏 

当程序执行过程中保留不再需要的内存时(主要是一些 变量、 方法等),会阻止内存被释放并导致程序的内存使用量随着时间的推移而增长,称为内存泄漏。

在 Vue.js 应用程序中,内存泄漏通常是由于组件、全局 EventBus、事件定时器 和 变量,函数引用的管理不当而引起的。

1. EventBus 引起的内存泄露

一个老生常谈的话题, Vue.js 中跨组件通信,要么是EventBus, 要么是Vuex 或者Pinia 这种数据流工具。

当我们对 EventBus 使用不当时,它们可能导致内存泄漏。
当组件被销毁时,应将它们从事件总线中删除,以防止延迟引用。

举个例子:

// 组件A.vue
<template>
  <div>
    <button @click="sendMessage">广播消息</button>
  </div>
</template>
<script>
import { EventBus } from "./EventBus.js";
export default {
  methods: {
    sendMessage() {
      EventBus.$emit("message", "Hello world from A!");
    }
  }
};
</script>
// 组件B.vue
<template>
  <div>
    <p>{{ receivedMessage }}</p>
  </div>
</template>
<script>
import { EventBus } from "./EventBus.js";
export default {
  data() {
    return {
      receivedMessage: ""
    };
  },
  created() {
    EventBus.$on("message", message => {
      this.receivedMessage = message;
    });
  }
};
</script>

在此示例中,发生内存泄漏是因为 ComponentB 从EventBus订阅了一个事件,但在该组件被销毁时并未取消订阅。

为了解决这个问题,我们需要在 组件B 的 beforeDestroy 钩子中使用 EventBus.$off 来删除事件监听器。

所以 需要对组件B做如下修改:

// ComponentB.vue
<template>
  <div>
    <p>{{ receivedMessage }}</p>
  </div>
</template>
<script>
import { EventBus } from "./EventBus.js";
export default {
  data() {
    return {
      receivedMessage: ""
    };
  },
  created() {
    EventBus.$on("message", message => {
      this.receivedMessage = message;
    });
  },
  beforeDestroy() {
    EventBus.$off("message"); //this line was missing previously
  }
};
</script>

2. 未被清理的定时器

Vue.js 应用程序中内存泄漏的最常见原因之一是未能正确删除定时器。当组件在其生命周期中使用了定时器,但并未合理的进行清除。

一旦组件被销毁, 定时器会继续引用该组件,从而防止其被垃圾收集。

举个例子:

<template>
   <div>
     <button @click="startShow">开始演示</button>
     <button @click="stopShow">停止演示</button>
   </div>
 </template>
 <script>
 export default {
   data() {
     return {
       intervalId: null
     };
   },
   methods: {
     startLeak() {
       this.intervalId = setInterval(() => {
         // Simulate some activity
         console.log("Interval running...");
       }, 1000);
     },
     stopLeak() {
       clearInterval(this.intervalId);
       this.intervalId = null;
     }
   }
 };
 </script>

在这个例子中,发生内存泄漏是因为单击 “开始演示” 按钮时创建了interval 定时,但在组件被销毁时没有正确清理它。

为了解决这个问题,我们需要在 beforeDestroy 生命周期中,对定时器进行清理。

所以最终的代码将如下所示:

<template>
   <div>
     <button @click="startShow">开始演示</button>
     <button @click="stopShow">停止演示</button>
   </div>
 </template>
 <script>
 export default {
   data() {
     return {
       intervalId: null
     };
   },
   methods: {
     startLeak() {
       this.intervalId = setInterval(() => {
         // Simulate some activity
         console.log("Interval running...");
       }, 1000);
     },
     stopLeak() {
       clearInterval(this.intervalId);
       this.intervalId = null;
     }
   },
@diff new
     beforeDestroy() {
     clearInterval(this.intervalId); // This line is missing above
   }
 };
 </script>

3. 第三方库使用不当

第三方类库使用不当,是内存泄漏的最常见原因。

这是由于组件清理不当造成的。这里我使用 Choices.js 库进行演示。

// cdn Choice Library
 <link rel='stylesheet prefetch' href='https://joshuajohnson.co.uk/Choices/assets/styles/css/choices.min.css?version=3.0.3'>
 <script src='https://joshuajohnson.co.uk/Choices/assets/scripts/dist/choices.min.js?version=3.0.3'></script>
 // our component
 <div id="app">
   <button
     v-if="showChoices"
     @click="hide"
   >Hide</button>
   <button
     v-if="!showChoices"
     @click="show"
   >Show</button>
   <div v-if="showChoices">
     <select id="choices-single-default"></select>
   </div>
 </div>
 // Script
 new Vue({
   el: "#app",
   data: function () {
     return {
       showChoices: true
     }
   },
   mounted: function () {
     this.initializeChoices()
   },
   methods: {
     initializeChoices: function () {
       let list = []
       // 创造更多的选项,方便直观的观察到内存泄漏
       for (let i = 0; i < 1000; i++) {
         list.push({
           label: "选项" + i,
           value: i
         })
       }
       new Choices("#choices-single-default", {
         searchEnabled: true,
         removeItemButton: true,
         choices: list
       })
     },
     show: function () {
       this.showChoices = true
       this.$nextTick(() => {
         this.initializeChoices()
       })
     },
     hide: function () {
       this.showChoices = false
     }
   }
 })

在上面的例子中,

我们加载了一个包含许多选项的下拉列表,然后使用带有 v-if 指令的显示/隐藏按钮来添加它并将其从虚拟 DOM 中删除。

此示例的问题在于 v-if 指令从 DOM 中删除了父元素,但我们没有清理 Choices.js 创建的额外 DOM 片段,从而导致内存泄漏。

我为大家做了一个在线的示例工程,便于大家去观察内存占用情况。

http://demolab.seanz.net/m-leak/index.html

当多点几次 之后,会有明确的内存增加。 附上一张 差异图。

初始化状态:

多次点击状态:

识别内存泄漏

识别 Vue.js 应用程序中的内存泄漏可能具有挑战性,因为它们通常表现为性能缓慢或随着时间的推移内存消耗增加。没有神奇的工具可以识别代码的问题所在。

但是,大多数现代浏览器都提供内存分析工具,允许您拍摄应用程序随时间的内存使用情况的快照。这些工具可以帮助您识别哪些对象消耗了过多的内存以及哪些组件没有得到正确的垃圾收集。

Chrome 的 "Heap Snapshot" 等工具可以通过可视化对象引用及其内存消耗来提供对内存使用情况的详细了解。

这可以帮助您更准确地查明内存泄漏的根源。

如何尽可能避免内存泄漏

  • 正确的使用计时器:确保在 Mounted 生命周期挂钩期间添加 计时器,在组件的 beforeDestroy 钩子中,进行清除。
  • EventBus :当你在组件中使用EventBus时,一定要确保,在组件销毁钩子方法中 将其 从 EventBus 中删除。
  • 响应式数据清理:在 beforeDestroy 生命周期钩子中,清理响应式数据属性,以防止它们保留对已销毁组件的引用。
  • 第三方类库:当使用在 Vue 之外操作 DOM 的其他 3rd 方库时,通常会发生这些泄漏。要修复此类泄漏,请正确遵循库文档并采取适当的措施。

总结

Vue.js 应用程序中的内存泄漏和性能测试可能很难识别和解决,而且在快速交付的兴奋中也很容易被忽视。

但是,保持较小的内存占用对于整体用户体验仍然很重要。

借助正确的工具、技术和实践,您可以显着减少遇到它们的机会。

通过正确管理一些明显引起内存泄漏的方法,您可以确保 Vue.js 应用程序以最佳性能运行并保持健康的内存占用。

以上就是Vue实现定位并解决内存泄漏的详细内容,更多关于Vue内存泄漏的资料请关注脚本之家其它相关文章!

相关文章

  • vue3中ref和reactive的区别举例详解

    vue3中ref和reactive的区别举例详解

    ref和reactive是Vue3中构建响应式数据的两大核心 API,虽然它们在使用上相似,但底层机制和适用场景有明显不同,这篇文章主要介绍了vue3中ref和reactive区别的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-06-06
  • 使用vue和datatables进行表格的服务器端分页实例代码

    使用vue和datatables进行表格的服务器端分页实例代码

    本篇文章主要介绍了使用vue和datatables进行表格的服务器端分页实例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Vue3中watchEffect的用途浅析

    Vue3中watchEffect的用途浅析

    这篇文章主要给大家介绍了关于Vue3中watchEffect用途的相关资料, watch和watchEffect都是监听器,但在写法和使用上有所区别,本文进行了详细的介绍,需要的朋友可以参考下
    2021-07-07
  • 在Vue组件上动态添加和删除属性方法

    在Vue组件上动态添加和删除属性方法

    下面小编就为大家分享一篇在Vue组件上动态添加和删除属性方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • 详解vue嵌套路由-params传递参数

    详解vue嵌套路由-params传递参数

    本篇文章主要介绍了详解vue嵌套路由-params传递参数,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Vue项目中使用jsonp抓取跨域数据的方法

    Vue项目中使用jsonp抓取跨域数据的方法

    这篇文章主要介绍了Vue项目中使用jsonp抓取跨域数据的方法,本文通过实例代码讲解的非常详细,需要的朋友可以参考下
    2019-11-11
  • Vue ECharts实现机舱座位选择展示功能代码详解

    Vue ECharts实现机舱座位选择展示功能代码详解

    这篇文章主要介绍了Vue ECharts实现机舱座位选择展示,本文给大家分享一段简短的代码通过效果图展示给大家介绍的非常明白,需要的朋友可以参考下
    2022-05-05
  • vue router仿天猫底部导航栏功能

    vue router仿天猫底部导航栏功能

    这篇文章主要介绍了vue router仿天猫底部导航栏功能,需要的朋友可以参考下
    2017-10-10
  • Vue3之使用js实现动画示例解析

    Vue3之使用js实现动画示例解析

    这篇文章主要为大家介绍了Vue3之使用js实现动画示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • vue实现轮播图的多种方式

    vue实现轮播图的多种方式

    这篇文章给大家介绍了vue实现轮播图的多种方式,文中给出了四种实现方式,并通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,感兴趣的朋友可以参考下
    2024-02-02

最新评论