浅谈VueJS SSR 后端绘制内存泄漏的相关解决经验


Posted in Javascript onDecember 20, 2018

引言

Memory Leak 是最难排查调试的 Bug 种类之一,因为内存泄漏是个 undecidable problem,只有开发者才能明确一块内存是不是需要被回收。再加上内存泄漏也没有特定的报错信息,只能通过一定时间段的日志来判断是否存在内存泄漏。大家熟悉的常用调试工具对排查内存泄漏也没有用武之地。当然了,除了专门用于排查内存泄漏的工具(抓取Heap之类的工具)之外。

对于不同的语言,各种排查内存泄漏的方式方法也不尽相同。对于 JavaScript 来说,针对不同的平台,调试工具也是不一样的,最常用的恐怕还是 Chrome 自带的各种利器(针对 browser 也好,nodeJS 也好)都有不错的使用体验,网上也有很多使用教程。

这次我想给大家介绍的内存泄漏的定位方法,并非工具的使用。而是一些经验的总结,也就是我所知道的 VueJS SSR 中最容易出现内存泄漏的地方,如果大家知道更多 VueJS SSR 内存泄漏点,可以在评论处留言告诉更多的人。

难点

遇到过 VueJS SSR 内存泄漏的朋友可能知道,针对 VueJS SSR 内存泄漏的排查,与普通 NodeJS 和 Browser 平台相比是要麻烦很多的。如果你使用了 webpack-dev-server 在本地调试,你会发现常用的内存泄漏工具毫无用武之地,因为抓取到的信息不仅包括 VueJS SSR 进程信息,还包含了 Webpack 的进程信息,甚至还有 webpack-dev-server 的各种堆信息。当然了,你也可以通过各种手段来过滤掉无关的信息,从而只剩下 VueJS SSR 的堆信息。

我在排查我们组项目内存泄漏的时候,动用了各种常规工具,但最终发现 VueJS SSR 的内存泄漏有很大可能性出现在以下地方,也就说如果,你碰巧也有 VueJS SSR 内存泄漏的问题,先不要使用内存泄漏排查工具,首先从下面几个地方着手,看看是否有内存泄漏的逻辑。可能直击要害,节约时间。

可能造成泄漏的位置

生命周期处的 beforeCreate/created

以下是 VueJS 开发者看过无数次的说明图,我还请大家再多看一遍

浅谈VueJS SSR 后端绘制内存泄漏的相关解决经验

在官方文档里,有这么一句话:

Since there are no dynamic updates, of all the lifecycle hooks, only beforeCreate and created will be called during SSR. This means any code inside other lifecycle hooks such as beforeMount or mounted will only be executed on the client.

也就是说 SSR 跟前端绘制一样,也有生命周期,只不过 SSR 的生命周期里只有 beforeCreate 和 created 。

所以你需要首先排查你的组件的 beforeCreate 和 created 里面是否有内存泄漏的代码,或者他们是否调用了会内存泄漏的代码。

路由守卫(Route Guards)处

路由也是会引起 SSR 内存泄漏的地方之一

跟生命周期不同,所有的 route guard 都会在 SSR 运行。他们分别都是

  • beforeEach
  • beforeRouteUpdate
  • beforeEnter
  • beforeRouteEnter
  • beforeResolve
  • afterEach
  • beforeRouteEnter

Data-Prefetch 处

还需要特别注意的地方就是 Date-prefetch 的地方,里面很容易出现内存泄漏的代码。 所谓 Date-prefetch 就是自定义实现的,在SSR处提前获取第三方数据,用于绘制的过程。

Global Mixin 处

这个内存泄漏的点想必大家都已经熟知,作者也在github上详细阐述过:GitHub issue

简单来说,就是 global mixin 会给每个 Vue 实例一个拷贝,而不是引用。

内存泄漏的例子

以上列举了一些可能出现内存泄漏的地方,那么具体怎么样的代码才会引起内存泄漏呢?引起代码泄漏的例子网上有很多,我在这里想给大家介绍几种常见的泄漏例子。

不小心造成的全局变量

function foo(arg) {
 bar = "this is a hidden global variable";
}

以上的代码会顺利运行,但是因为不小心声明了一个 bar 的变量。相当于:

function foo(arg) {
 window.bar = "this is an explicit global variable";
}

生成了一个全局变量 window.bar

如果不手动回收,这个全局变量会一直存在于内存中,不会被CG回收。积少成多,最后造成内存泄漏。

现在大家都是在各种模块化(CommonJS/AMD/CMD/etc..)之后的环境下进行开发,这种全局变量的内存泄漏的问题基本上是被消除了。但是要提醒大家,由于JavaScript的各种特性,会有很多意想不到的状况发生。当摸不清头脑的时候,可以尝试从这些特性出发找到问题。

被遗忘了的 Timer 或者 callback

请大家先看以下的例子

var someResource = getData();
setInterval(function() {
 var node = document.getElementById('Node');
 if(node) {
 // Do stuff with node and someResource.
 node.innerHTML = JSON.stringify(someResource));
 }
}, 1000);

乍一看没啥问题,之后如果 Node 节点从DOM上被移除,因为上面的 callback 对 Node 节点有引用,所以 Node 节点会一直常驻内存,不会被CG回收。

要避免以上问题,就要养成 removeEventListenerclearInterval 的习惯。

var someResource = getData();
var interval = setInterval(function() {
 var node = document.getElementById('Node');
 if(node) {
 // Do stuff with node and someResource.
 node.innerHTML = JSON.stringify(someResource));
 } else {
 // Remove Timer
 clearInterval(interval);
 }
}, 1000);

还比如:

var element = document.getElementById('button');

function onClick(event) {
 element.innerHtml = 'text';
}

element.addEventListener('click', onClick);
// Do stuff
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
// Now when element goes out of scope,
// both element and onClick will be collected even in old browsers that don't
// handle cycles well.

addEventListener 之后已经要记得 removeEventListener

闭包

闭包造成内存泄漏的情况比较复杂,而且较难查找。限于本文主旨,不做原理说明。

总结

个人认为 VueJS SSR 后端绘制内存泄漏造成影响要比普通的 VueJS 前端内存泄漏造成的影响要更大。

前端内存泄漏的影响,都是发生在客户机器上,而且基本上现代浏览器也会做好保护机制,一般自行刷新之后都会解决。但是,一旦后端绘制内存泄漏造成宕机之后,整个服务器都会受影响,危险性更大,搞不好年终奖就没了。

前端工程师一般都是关注于浏览器端表现,在开发过程中的内存泄漏问题不太在意也不太容易被发现。一般都是在项目上线一段时间之后,才发现内存泄漏的情况。那个时候再去着手,可能会有些无从下手或者手忙脚乱。

那么,就让我们在开发的时候开始关注内存泄漏问题,将 VueJS SSR 后端绘制内存泄漏问题扼杀于襁褓之中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery帮助之CSS尺寸(五)outerHeight、outerWidth
Nov 14 Javascript
手把手教你自己写一个js表单验证框架的方法
Sep 14 Javascript
从盛大通行证上摘下来的身份证验证js代码
Jan 11 Javascript
IE下Ajax缓存问题的快速解决方法(get方式)
Jan 09 Javascript
jQuery oLoader实现的加载图片和页面效果
Mar 14 Javascript
javascript每日必学之多态
Feb 23 Javascript
探索angularjs+requirejs全面实现按需加载的套路
Feb 26 Javascript
js 倒计时(高效率服务器时间同步)
Sep 12 Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
Jul 27 Javascript
详解BootStrap表单验证中重置BootStrap-select验证提示不清除的坑
Sep 17 Javascript
layui layer select 选择被遮挡的解决方法
Sep 21 Javascript
WebStorm无法正确识别Vue3组合式API的解决方案
Feb 18 Vue.js
Cocos2d实现刮刮卡效果
Dec 20 #Javascript
浅谈Fetch 数据交互方式
Dec 20 #Javascript
cocos2dx+lua实现橡皮擦功能
Dec 20 #Javascript
element-ui table span-method(行合并)的实现代码
Dec 20 #Javascript
fetch 如何实现请求数据
Dec 20 #Javascript
JS闭包经典实例详解
Dec 20 #Javascript
JS闭包原理与应用经典示例
Dec 20 #Javascript
You might like
PHP5.3的垃圾回收机制(动态存储分配方案)深入理解
2012/12/10 PHP
详解WordPress中创建和添加过滤器的相关PHP函数
2015/12/29 PHP
laravel框架路由分组,中间件,命名空间,子域名,路由前缀实例分析
2020/02/18 PHP
javascript textContent与innerText的异同分析
2010/10/22 Javascript
js getBoundingClientRect() 来获取页面元素的位置
2010/11/25 Javascript
jQuery 瀑布流 绝对定位布局(二)(延迟AJAX加载图片)
2012/05/23 Javascript
在jQuery ajax中按钮button和submit的区别分析
2012/10/07 Javascript
JS+CSS设置img在DIV中只显示Img垂直居中的部分
2013/10/24 Javascript
js实现特定位取反原理及示例
2014/06/30 Javascript
js实现的星星评分功能函数
2015/12/09 Javascript
JQuery中attr属性和jQuery.data()学习笔记【必看】
2016/05/18 Javascript
Bootstrap 过渡效果Transition 模态框(Modal)
2017/03/17 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
jQuery选择器中的特殊符号处理方法
2017/09/08 jQuery
jQuery选择器之表单元素选择器详解
2017/09/19 jQuery
JS去掉字符串末尾的标点符号及删除最后一个字符的方法
2017/10/24 Javascript
vue.js开发实现全局调用的MessageBox组件实例代码
2017/11/22 Javascript
17道题让你彻底理解JS中的类型转换
2019/08/08 Javascript
vuex管理状态 刷新页面保持不被清空的解决方案
2019/11/11 Javascript
[01:06:30]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第二场 1月9日
2021/03/11 DOTA
python实现树形打印目录结构
2018/03/29 Python
python 通过logging写入日志到文件和控制台的实例
2018/04/28 Python
python获取中文字符串长度的方法
2018/11/14 Python
python常用函数与用法示例
2019/07/02 Python
解决Django加载静态资源失败的问题
2019/07/28 Python
python 写函数在一定条件下需要调用自身时的写法说明
2020/06/01 Python
python3+openCV 获取图片中文本区域的最小外接矩形实例
2020/06/02 Python
Python执行时间的几种计算方法
2020/07/31 Python
台湾旅游网站:灿星旅游
2018/10/11 全球购物
英国门把手公司:Door Handle Company
2019/05/12 全球购物
Arti-shopping中文官网:大型海外商品一站式直邮平台
2020/03/23 全球购物
伦敦鲜花递送:Flower Station
2021/02/03 全球购物
设计模式的基本要素是什么
2014/04/21 面试题
J2EE面试题集锦(附答案)
2013/08/16 面试题
公司员工宿舍管理制度
2015/08/03 职场文书
windows server2012 R2下安装PaddleOCR服务的的详细步骤
2022/09/23 Servers