ResizeObserver loop limit exceeded

欢迎大家阅读,刚在老家入职新公司第七天就遇到了这个问题,赶紧想办法解决…

在这里插入图片描述

问题原因

在使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厉害坤坤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值