0

0

vue使用中的内存泄漏【推荐】_vue.js

无忌哥哥

无忌哥哥

发布时间:2018-07-12 14:04:31

|

1653人浏览过

|

来源于php中文网

原创

内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。这篇文章主要介绍了vue使用中的内存泄漏,需要的朋友可以参考下

什么是内存泄露?内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。new了一个对象之后,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作用域导致被销毁,那么这块内存没有人引用它了在JS里面就会被自动垃圾回收。但是如果这个对象指针没有被置为null,且代码里面没办法再获取到这个对象指针了,就会导致无法释放掉它指向的内存,也就是说发生了内存泄露。为什么代码里面会拿不到这个对象指针了呢,举一个例子:

// module date.js
let date = null;
export default {
 init () {
  date = new Date();
 }
}
// main.js
import date from 'date.js';
date.init();

在main.js初始化了date之后,date这个变量就一会直存在了,直到你把页面关了,因为date的引用是在另一个module里面,可以理解为模块就是一个闭包对外是不可见的。所以如果你是希望这个date对象一直存在、需要一直使用的话,那么没有问题,但是如果想用一次就不用了那就会有问题,这个对象一直在内存里面没有被释放就发生了内存泄露。

另一种比较隐蔽并且很常见的内存泄露是事件绑定,形成了一个闭包,导致一些变量一直存在。如下例子所示:

立即学习前端免费学习笔记(深入)”;

// 一个图片懒惰加载引擎示例
class ImageLazyLoader {
 constructor ($photoList) {
  $(window).on('scroll', () => {
   this.showImage($photoList);
  });
 }
 showImage ($photoList) {
  $photoList.each(img => {
   // 通过位置判断图片滑出来了就加载
   img.src = $(img).attr('src');
  });
 }
}
// 点击分页的时候就初始化一个图片懒惰加载的
$('.page').on('click', function () {
 new ImageLazyLoader($('img.photo'));
});

这是一个图片懒惰加载的模型,每次点分页的时候就会清掉上一页的数据更新为当前页的DOM,并重新初始化一个懒惰加载的引擎。它里面监听了scroll事件,对传进来的图片列表的DOM进行处理。每点一次分页就会重新new一个,这里就发生了内存泄露,主要是以下3行代码导致的:

$(window).on('scroll', () => {
 this.showImage($photoList);
});

因为这里的事件绑定形成了一个闭包,this/$photoList这两个变量一直没有被释放,this是指向ImageLazyLoader的实例,而$photoList是指向DOM结点,当清除掉上一页的数据的时候,相关DOM结点已经从DOM树分离出来了,但是仍然还有一个$photoList指向它们,导致这些DOM结点无法被垃圾回收一直在内存里面,就发生了内存泄露。由于this变量也被闭包困住了没有被释放,所以还有一个ImageLazyLoader的实例发生内存泄露。

这个的解决方法比较简单,就是销毁实例的时候把绑定的事件off掉,如下代码所示:

class ImageLazyLoader {
 constructor ($photoList) {
  this.scrollShow = () => {
   this.showImage($photoList);
  };
  $(window).on('scroll', this.scrollShow);
 }
 // 新增一个事件解绑       
 clear () {      
  $(window).off('scroll', this.scrollShow);
 }
 showImage ($photoList) {
  $photoList.each(img => {
   // 通过位置判断图片滑出来了就加载
   img.src = $(img).attr('src');
  });
  // 判断如果图片已全部显示,就把事件解绑了
  if (this.allShown) {
   this.clear();
  }
 }
}
// 点击分页的时候就初始化一个图片懒惰加载的
let lazyLoader = null;
$('.page').on('click', function () {
 lazyLoader && (lazyLoader.clear());
 lazyLoader = new ImageLazyLoader($('img.photo'));
});

在每次实例化一个ImageLazyLoader之前把先把上一个实例clear掉,clear里面进行解绑,由于JS有构造函数但是没有解构函数,所以需要自己写一个clear,在外面手动调一下clear。同时在事件的执行过程的合适时机自动把事件给解绑了,上面是判断如果所有的图片都展示出来了那么就没必要监听scroll事件了直接解绑了。这样就能解决内存泄露的问题了,能够触发自动垃圾回收。

为什么把事件解绑了,就不会有闭包引用了呢?因为JS引擎检测到那个闭包没用了,就把那个闭包销毁了,那么闭包引用的外部变量也自然会被置空。

好了,基础知识就讲解到这里,现在用Chrome devtools的内存检测工具来实际操作一遍,方便发现页面的一些内存泄露行为。为了避免装给浏览器装的一些插件造成影响,使用Chome的隐身模式页面,它会把所有的插件都给禁掉。

然后打开devtools,切到Memory的tab,选中Heap snapshot,如下所示:

 

什么叫heap snapshot呢?翻译一下就是堆快照,给当前内存堆拍一张照片。因为动态申请的内存都是在堆里面的,而局部变量是在内存栈里面,是由操作系统分配管理的是不会内存泄露了。所以关心堆的情况就好了。

然后做一些增删改DOM的操作,如:

(1)弹一个框,然后把弹框给关了

(2)单页面的点击跳转到另一个路由,然后再点后退返回

(3)点击分页触发动态改DOM

就是先增加DOM,然后把这些DOM给删了,看一下这些被删除的DOM是否还有对象引用它们。

这里我是第2种方式的场景,检测单页面应用的某个路由页面是否存在内存泄露。先打开首页,点到另一个页面,再点后退,接着点一下垃圾回收的按钮:

吉卜力风格图片在线生成
吉卜力风格图片在线生成

将图片转换为吉卜力艺术风格的作品

下载

 

触发垃圾回收,避免一些不必要的干扰。

然后再点一下拍照按钮:

 

它就会把当前页面的内存堆扫描一遍显示出来,如下图所示:

 

然后在上面中间的Class Filter的搜索框里搜一下detached:

 

它就会显示所有已经分离了DOM树的DOM结点,重点关注distance值不为空的,这个distance表示距离DOM根结点的距离。上图展示的这些p具体是啥呢?我们把鼠标放上去不动等个2s,它就会显示这个p的DOM信息:

 

通过className等信息可以知道它就是那个要检查的页面的DOM节点,在下面的Object的窗口里面依次展开它的父结点,可以看到它最外面的父结点是一个VueComponent实例:

 

下面黄色字体native_bind表示有个事件指向了它,黄色表示引用仍然生效,把鼠标放到native_bind上面停留2秒:

它会提示你是在homework-web.vue这个文件有一个getScale函数绑定在了window上面,查看一下这个文件确实是有一个绑定:

mounted () {
 window.addEventListener('resize', this.getScale);
}

所以虽然Vue组件把DOM删除了,但是还有个引用存在,导致组件实例没有被释放,组件里面又有一个$el指向DOM,所以DOM也没有被释放。

要在beforeDestroyed里面解绑的

beforeDestroyed () {
 window.removeEventListener('resize', this.getScale);
}

所以综合上面的分析,造成内存泄露的可能会有以下几种情况:

(1)监听在window/body等事件没有解绑

(2)绑在EventBus的事件没有解绑

(3)Vuex的$store watch了之后没有unwatch

(4)模块形成的闭包内部变量使用完后没有置成null

(5)使用第三方库创建,没有调用正确的销毁函数

并且可以借助Chrome的内存分析工具进行快速排查,本文主要是用到了内存堆快照的基本功能,读者可以尝试分析自己的页面是否存在内存泄漏,方法是做一些操作如弹个框然后关了,拍一张堆快照,搜索detached,按distance排序,把非空的节点展开父级,找到标黄的字样说明,那些就是存在没有释放的引用。也就是说这个方法主要是分析仍然存在引用的游离DOM节点。因为页面的内存泄露通常是和DOM相关的,普通的JS变量由于有垃圾回收所以一般不会有问题,除非使用闭包把变量困住了用完了又没有置空。

DOM相关的内存泄露通常也是因为闭包和事件绑定引起的。绑了(全局)事件之后,在不需要的时候需要把它解绑。当然直接绑在p上面的可以直接把p删了,绑在它上面的事件就自然解绑了。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

89

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

276

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

59

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

99

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

105

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

230

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

619

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

173

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue 教程
Vue 教程

共42课时 | 9.6万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号