浏览器崩溃问题解决

背景

我们部门有个项目是基于metersphere(技术栈是vue)这个开源项目二次开发的,这个项目有个问题,使用过程中总是会出现浏览器崩溃问题,而且我们公司的测试同学也反馈有这个问题,一天能崩溃7、8次,很影响工作,之前也有同事尝试处理这个问题,发现在使用过程中,内存一致在上涨,判断是内存溢出造成的浏览器崩溃,然后就通过查看代码发现在操作过程中有两个递归函数一直在调用,就优化了这两个递归函数,优化后内存可以正常回收,但是客户反馈还是总是出现崩溃的情况,后面我接手了这个项目也开始处理这个问题。

解决过程

要解决一个bug最方便的是先复现问题,然后根据现象对照代码然后修复。遇到不好复现的问题也可以先排查代码,根据代码中发现的问题复现bug,然后修复。

定位问题

因为这个问题不好复现,所以我在排期的前一周就和测试同学约定好遇到浏览器崩溃时注意的信息,然后根据测试同学的反馈总结:这个问题主要出现的接口自动化的用例详情页面,在调试用例或保存时经常出现,没有稳定复现的操作过程。看到结论我的感受是:哇,这都哪跟哪啊,这是什么神仙问题,该从哪入手,我该怎么复现;那看看代码吧,这部分代码有几千行,涉及几十个组件,更没法查。先从代码入手是不现实的,所以还是先点点页面吧。先找到一个数据量大的用例用一下吧,先试试调试,保存这些功能,发现没什么问题,那就看看这个用例里面有什么吧,因为用例有点大(用例是一个树形结构,一层一层的,每一层都会有很多接口、用例、控制器等,代码中是使用element-ui中的自定义树形控件实现的),展开的时候耗时比较长,展开后上下滑动还挺正常,收起再展开滑动就开始卡顿了,第三次就出现崩溃情况了,我眼睛顿时一亮:你完了,我找到你了。好嘞,先看一下页面基本情况:

  1. 先查一下页面中一共有多少个元素:
    控制台中显示页面中有18万个标签
    一个页面中可以有这么多元素吗?涨知识了
  2. 再顺着之前的解决思路看一下内存消耗:memory中显示已占用1748M资源
    一个网页竟然可以占这么多内存?涨知识了
  3. 然后关闭所有步骤再展开,看一下内存消耗:通过performance查看发现有内存回收情况单占用的内存持续上涨,
    哦豁,占用的内存翻倍了,先看一下步骤树的展开和收起什么逻辑。

结合代码看收起步骤树的处理逻辑:这个树形控件通过v-if指令先为false再为true,达到收起树的所有节点的效果,在vue中v-if指令的值为false时对应的组件会被销毁,但是从内存使用情况看,原先被卸载的树形控件占的内存没回收,由此判断应该是树形控件这部分收起后没卸载完全,导致内存泄漏,测试同学在使用过程中频繁展开收起就会造成占用的内存越来越多,最终浏览器崩溃。好了,找到方向了,开搞开搞。

解决问题

既然可能是步骤树出了问题,那就先排查一下步骤树。先把步骤树注释掉,然后写一个自调用的组件,目的是占用大量内存,减少其他因素对内存回收效果显示的影响,然后将步骤树中的v-if指令放到自调用组件上,使用展开收起功能发现内存可以正常回收,由此可以证明确实是步骤树这部分中存在内存泄漏。
给自定义组件的v-if指令赋的值为false1后有个明显的内训下降效果
好嘞,接下来的工作就是去排查哪些组件的哪些代码造成的了内存泄漏。我的工作方法是将组件注释,然后看注释后展开收起步骤树情况下内存回收的情况,找到问题并解决。

问题一:

经过排查发现有些组件通过 addeventListener 向 window 上注册事件后没有进行合理的卸载,这种就是典型的会引起内存泄漏的做法如:
在这里插入图片描述
解决这种问题的方法是一定要在合适的生命周期中通过 removeEventListener 卸载对应的方法,本组件中是在 beforeDestory 钩子函数中添加的 removeEventListener 操作,此时收起步骤条后内存可以正常回收。解决完 addeventListener 引起的内存泄漏后,将原来注释的组件还原,发现内存又不能正常回收了,看来注释的组件中还有其他问题。

问题二:

在排查时还发现了一个难理解的问题:使用 element-ui 中的 input 组件并调用 focus 事件,也会出现内存泄漏,但是在相同状态下使用 input 标签并调用 focus 事件就没事,初步排查了element-ui 的源码,但是没发现什么异常代码,所以就取消了 focus 事件的调用。

问题三:

在解决完上面两个问题后,在本地的开发环境中展开和收起步骤树,内存可以正常回收,多次点击也没出现浏览器崩溃的情况,然后就打包发布到测试环境,但是在测试环境中还是内存回收,这种情况应该跟代码的环境配置有关,所以就用生产模式在本地启动项目,这种模式下启动的项目确实也是内存不回收,经过排查发现是组件内有的组件通过按需引入的方式引入就会造成这个问题,但是按需引入是官方运行的方式,而且也是很正常的代码,所以就需要排查打包后的代码是什么逻辑。为了排查打包后的代码就要在本地启一个服务器,然后将打包后的代码放在这个服务器中,在通过浏览器打开,这样就能调试打包后的代码了。经过排查发现在按需引入的组件中,如果有style标签并且这个标签内有内容则会将这个style标签放入一个css文件中,渲染这个按需引入的组件时会加载组件对应的js文件,并且加载这个css文件,但是加载css文件时有个error状态的处理函数,这个处理函数是直接定义给error状态的,这种定义方式也是会出问题的(图中红框中的定义方式都有问题,绿框是正常的)
在这里插入图片描述
这是生产模式下打包出来的代码不是业务相关的,不推荐修改。所以就查了vue脚手架生产模式和开发模式的配置区别,发现是extract-css-loader引起的
在这里插入图片描述
然后通过查看源码并结合官方文档,发现可以通过添加了css.extract属性用来控制css的引入方式。添加完配置打包发布测试,展开和收起步骤树内存终于正常回收了。ok上线。

总结

解决完点击展开收起功能引起的内存泄漏问题后,又排查了关闭用例详情和切换页面这两种场景的内存泄漏,然后又发现了两种情况:

  1. 第三方组件vue-float-action-button(浮动操作按钮)中的addeventListener没有对应的removeEventListener,解决方案:复制出来vue-float-action-button源码,放入项目中然后修改
  2. vue-route中的router-view通过v-if重新渲染后,原来的router-view中的组件虽然被重新加载,但是内存没回收,解决方案:通过 window.location.reload() 方法重新加载页面

在处理这些问题的过程中,有些问题是很容易解决并且原理很简单,但是有些问题就很难理解,找了很对资料也没解惑,只能通过一位前端大佬的话“已经知道怎么规避了,对于业务来说价值到了”聊以自慰。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值