欢迎大家阅读,刚在老家入职新公司第七天就遇到了这个问题,赶紧想办法解决…
问题原因
在使用vue3+TS+elementui-plus开发低代码平台时,遇到了上述问题。看了些文章并结合报错信息,确定是因为 element-plus 的 el-table引起的。ps:antd也可能会遇到此类问题,毕竟都是大同小异的东西…
继续向下定位,思考了下本质:在elementui升级之后(具体版本:自2.3.5版本后,Table-column取消了默认宽度大小,增加了 type参数),如果在一个动画帧内,ResizeObserver不能处理所有的observations,就会触发这个ResizeObserver loop limit exceeded,我理解的这样设计的原因是对性能的一个要求吧。
常规解决办法
开始在度娘上阅读了一些文章,基本都是转发的,大致只有两种解决办法,哦对,不是解决,而是用笨方法去规避这个问题,下面先介绍这两种办法,最后再献上我最终的方法,这也是我整体的一个思路。
方法一:防抖
这是网上用的最多的办法:
- 在main.js/ts || app.vue中(app.vue写在script里面 main.js写在app挂载完成之后),利用window.ResizeObserver方法和防抖函数,实现在table绘制时减少帧数。
- 这个方法好处是参与热更新,不用重新npm run serve,table渲染性能得到提升,因为不会让table不停绘制了;弊端是防抖函数设置了timer,这里设置为10,但是在页面table渲染的时候还是会有0.01秒的闪现,即使是0.01秒闪现还是很好发现的,改大点数字可以测试看看
具体实现如下(js、ts两个版本):
//main.js
const debounce = (fn, delay) => {
let timer
return (...args) => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
}, delay)
}
}
const _ResizeObserver = window.ResizeObserver;
window.ResizeObserver = class ResizeObserver extends _ResizeObserver{
constructor(callback) {
callback = debounce(callback, 10);
super(callback);
}
}
//main.ts
const debounce = (fn: Function, delay: number) => {
let timer: any
return (...args: any) => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
}, delay)
}
}
const _ResizeObserver = window.ResizeObserver;
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
constructor(callback: ResizeObserverCallback) {
super(debounce(callback, 10));
}
}
方法二:强制忽略此报错信息
- 这个办法比较暴力,直接修改vue.config.js配置。
- 这个办法的好处是直接忽略报错信息,也就是说不光动态列出问题时,比如将浏览器拖拽屏幕产生滚动条时等等一系列需要重新绘制table导致此问题时,这个办法都可以解决;弊端是不参与热更新,需要重新npm run serve才能看到效果,性能没有提升,只是屏蔽了错误
直接上代码(一看就懂):
module.exports = defineConfig({
...
devServer: {
client: {
overlay: {
warnings: false,
runtimeErrors: (error) => {
const ignoreErrors = [
"ResizeObserver loop limit exceeded",
"ResizeObserver loop completed with undelivered notifications.",
];
if (ignoreErrors.includes(error.message)) {
return false;
}
},
},
},
},
});
我的最终解决方案思考
以上是这两个常用的办法,正好今天工作任务完成了,就来写一写我自己的理解和解决办法:
首先通过查阅文章和资料,我们大致明白出现这个错误的原因了:
1.动态设置列(通过接口请求得到header,列的数量不固定),table渲染时,elementui-plus需要并行计算表头和数据的宽高(这是本文主要解决的问题,2和3还没有遇到和测试过,以后遇到的话我再更新文章)
2.将浏览器拖拽屏幕时可能引发重新计算宽高
3.其他重绘elementui组件时都经常出现此错误
本质上我的理解就是在重绘table时,或者部分样式引起dom容器大小不固定时,window.ResizeObserver方法调用频率过高,浏览器抛出的错误(有人说只有Chrome报这个错,还未试试其他浏览器)。
那么结合一下我在项目中遇到的动态列,说明我在得到有哪些列时,table得到header数据和data数据,之前固定列是通过table-column属性设置好的,所以不容易出现这个问题。那么我是不是可以在请求完数据的时候,在nextTick方法中对table做一个绘制的调整,因为我正好在elementui-plus table文档中发现了el-table有一个方法叫doLayout(官方解释是:对 Table 进行重新布局。 当表格可见性变化时,您可能需要调用此方法以获得正确的布局),正好符合我的需求啊,心中一道灵光闪现,开撸啊兄弟们(撸代码):
import { ElTable } from 'element-plus'
const sqlTableRef = ref<InstanceType<typeof ElTable>>()
//拿到数据时
nextTick(() => {
sqlTableRef.value!.doLayout()
});
<el-table ref="sqlTableRef">
完整代码:
<script lang="ts" setup>
import {onMounted, ref, nextTick} from "vue";
import axios from "axios";
import {ElMessage} from "element-plus";
import {ElTable} from 'element-plus'
let list = ref<sqlRunData[]>([]);
let tableHeader = ref([]);
const tableRef = ref<InstanceType<typeof ElTable>>()
const run = () => {
axios.post("/XXX", {}).then(res => {
let responseData = res.data;
if (responseData.success) {
if (responseData.data.tableHeader != null) {
tableHeader.value = responseData.data.tableHeader
list.value = responseData.data.sqlRunResultList
nextTick(() => {
tableRef.value!.doLayout()
});
} else {
ElMessage.error('XXXXXX');
}
} else {
ElMessage.error(responseData.message);
}
}, error => {
ElMessage.error(error.message)
})
}
onMounted(() => {
run()
})
</script>
<template>
<div class="main">
<div class="table">
<el-table :data="list" ref="tableRef">
<el-table-column v-for="column in tableHeader" :key="column" :label="column" :prop="column">
</el-table-column>
</el-table>
</div>
</div>
</template>
<style lang="less" scoped>
</style>
上面讲到,防抖函数虽然解决了问题,提升了性能,但是体验并不是很好,因为毕竟table会出现闪现的问题,也就是说至少绘制了两遍都被我们看在眼里,第一遍只绘制了一部分,因此会有闪现的效果。因此在nextTick中绘制table起码保证了利用eventloop的特性,这样就不会出现ResizeObserver loop limit exceeded 错误和闪现的问题了,亲测有效。
有时间我会继续思考此类问题,欢迎大家讨论或指正。
本文原创,转载请注明出处:https://blog.csdn.net/l2025043/article/details/137642497