起因是用户反馈网页操作久了后,页面会出现白屏情况。 项目主要包版本:
- vue-cli4
- vue2.6
- element-ui 2.15
解决白屏问题
复现过程中发现页面刷新后,详情页面的路由点击都变成了白屏。
排查过程中发现:
- 这是正常情况的详情路由
- 页面刷新后变成了
原因:页面刷新后,所有会被路由清空,重新进入页面时会重新请求菜单数据追加路由。但是详情路由并不是从后端获取的,而是在后续操作过程中新加入的,所以刷新后数据丢失了,路由找不到,才会白屏。
解决思路:
将路由缓存下来,这样页面刷新后先读取缓存数据。解决过程中发现,我们的 tab 标签数据【这个数据里面有当前路由对应的组件地址】是有缓存的,那么我们就可以给后加入的路由一个标志,初始化路由的时候,看看有没有缓存的 追加的 tab 数据,有就拼接路由。
白屏解决,刷新后页面,详情页面恢复。
解决页面崩溃问题
解决了白屏问题后,用户反馈,详情页面自己修改操作的一些数据又没有了,有时候页面还出现了崩溃现象。一番挣扎后发现,都是页面内存占用过高惹的祸。主要的占用就是el-table
引起的,现象:
- 表格数量多,表格内部结构复杂
- 用户老喜欢一次性查询1000条数据
- 后端返回的数据体积超大,10M起
主要的影响因素
排查思路:
- 表格数据是否回收
- el-dialog 数据是否回收
- keep-alive 移除后数据时候回收
表格数据回收问题
表格数据清空后,Detached <div>
中出现了大量的el-popover
元素。查阅一番发现,vue中模态框就会出现内存泄漏问题。而我们的表格中,每一行基本都有n个el-popover,那整个表格不就是1000n个。而且popover会产生大量的真实dom,会对页面渲染造成卡吨。既然已经要泄漏了,那么就将泄漏1000n变成1个吧。
解决方向: 表格所有需要触发popover的元素都共用一个popover。
- 改造现有el-popover,我们直接使用
v-if
还是会造成内存泄漏,所以使用组件内部的方法去销毁他。使用继承是方便直接使用里面的属性、方法。
vue
<script>
import { Popover } from "element-ui"; // 引入element的popover组件
import Vue from "vue"; // 引入vue
export default Vue.extend({ // Vue.extend是vue中的api,用于继承组件
extends: Popover, // 指定要继承的组件
methods: {
popBy(el) {
// 先隐藏并销毁之前显示的
this.close();
this.doDestroy(true);
this.$nextTick(() => {
// 显示新的 referenceElm 是触发显示popover的元素
this.referenceElm = this.$refs.reference = el;
this.$nextTick(() => {
this.showPopper = true;
this.$emit("input", true);
});
});
},
close() {
this.showPopper = false;
this.$emit("input", false);
}
}
});
</script>
- customPop使用
xml
<template>
<customPop
ref="popoverRef"
popper-class="table-item-popover"
placement="top"
width="542"
@show = "show"
@hide = "hide"
>
<!-- popover 展示内容 -->
</customPop>
</template>
<script>
import customPop from "./../../../../customPop";
export default {
name: "tablePopover",
components: { customPop },
data() {
return {
showPop: false,
};
},
methods: {
open(e, content) {
this.showPop = true;//便于判断popover 是否打开
this.$refs.popoverRef.popBy(e);
},
show() {
// 需要赋值的逻辑在这个处理 customPop会先销魂再创建 popover的hide会在打开后出发一次
},
close() {
this.$refs?.popoverRef?.close();
},
hide() {
// 将动态创建的dom 都清除
this.showPop = false;
this.relationFormGroups = [];
},
destory() {
try {
this.$refs?.popoverRef?.doDestroy(true);
} catch (e) {
console.error("表格popover", e);
}
}
},
beforeDestroy() {
this.destory();
}
};
</script>
- 表格统一调用
vue
<tempalte>
<tablePopover ref="tablePopover"/>
</template>
<script>
export default{
data(){
popel:null
},
methods:{
openPover(e){
if(this.popel===e.target){//同一元素不触发
return;
}
this.$refs['tablePopover']?.open(e.target);
}
}
}
</script>
- 效果 1000千数据共32M,内存占用1.9G降低至1.35G。如果可以的话,把tableData也就是表格数据用Object.freez()冻结,内存能再降低一些。
el-dialog 回收问题。
el-dialog 也是模态框所以也会有会内存泄漏问题,这个问题目前没有找到比较好的根本解决方法。缓解方法就是将动态渲染出来的dom,在弹框关闭时先将数据清空然后再关闭。
keep-alive 移除问题
测试发现,表格加载页面后,如果页面没有出现dom更新,也就页面没有出现过新的元素,如输入框输入内容,打开弹框等,关闭后就能回收,表格也不会泄漏。但是一旦有元素变化,那表格内容就都泄漏了。但是如果重新回到关闭的那个页面,内存的泄漏又会减少。目前还没有很好的解决方法。