通过Vs Code调试,我找到了Ant Design Table运行报错问题~

前言

今天产品反馈表格样式在低分辨率屏幕下样式错位问题

那太简单了,给antd table加上scroll属性不就行了?并且给指定Column加上width,于是:

jsx 复制代码
<Table
  dataSource={list}
  loading={loading}
  rowKey={(record) => record.id}
  pagination={false}
  size="middle"
  scroll={{
    x: 1200
  }}
>
<Table.Column 
  dataIndex="aaa" 
  width={120} 
  title="aaa" 
/>
</Table>

但运行之后给我报了这个错

这个是浏览器提供的 JavaScript API,允许开发者监视 DOM 元素的大小变化。

对于动态生成的表格、图表或其他复杂的交互式元素,ResizeObserver 可以帮助监测容器大小的变化,以便调整表格列宽、图表尺寸或者重新渲染数据。这是它在Antd中的作用。

先不管,去github issue看看啥情况,还真有几条:

点进去看了并没有一个完美的解决方案,同时我从中get到了一个信息,这个错误是由于rc-table导致的

于是我产生产生了疑问:什么是rc-tableantd table跟它有什么关系?

我重新去MDN看了这个API的说明,它还给出相关错误解释:

developer.mozilla.org/en-US/docs/...

上面的代码表示如果循环修改元素的widthResizeObserver在计算尺寸的同时,节点的宽度依旧发生变化,浏览器不允许你这样操作,导致抛出这个错误。

带着这些已知信息,我决定翻一翻antd源码看看。

Vs Code调试

React项目中创建launch.json,通过launch启动3000端口的调试服务

json 复制代码
{
  // 使用 IntelliSense 了解相关属性。 
  // 悬停以查看现有属性的描述。
  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "针对 localhost 启动 Chrome",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}"
    }
  ]
}

我使用的是class组件来引入table组件,要知道antd组件是函数式组件,它们在解析jsx是不一样的,class组件通过updateClassComponent来创建DOM,在render中打个断点试试:

通过调用栈就可以看到是通过beginWork来调度不同类型的组件方法,点进去看看:

可以看到这里是通过Switch...Case来做分支,要想看到table组件的执行过程,找到updateFunctionComponent中的renderWithHooks方法有这段代码:

所有的函数式组件都会在这里处理,打个断点试试:

页面中这么多组件,怎么找到Table呢,一个个点下去太慢了,我们通过条件断点过滤一下:

释放断点,可以看到就在ant table组件停下来:

点击单步调试进入子函数中,来到的就是table组件编译之后的源码中

这个文件中,的确看到了之前说的rc-tableant table是基于这个table扩展的,最后创建的就是RcTable这个组件

找到这个包下面的es/Table.js文件,打一个断点

可以看到的确是table传过来的Props,默认dataSource[], 往下面可以看到,在这里创建Table Body节点的:

进入body文件,可以看到这里是根据props传递的列表数据长度来创建行:

而初始化是dataSource为空,进入创建ExpandedRow组件

可以看到,这里进行创建真实节点,绑定的stylewidth的宽度是动态的,这就有可能造成上面的问题。

现在问题是从哪里监听元素变化的呢?这就要回到rc-table中的代码了:

重新进行断点,horizonScrolltrue说明我们表格的确是横向滚动的

这里会依赖rc-resize-observer包中的组件ResizeObserver,最终会去到observerUtil.js工具方法中,这里定义了监听逻辑

再来验证一下,我监听DIV节点,看是否可能进入到这里

可以看到,这里捕获了DIV节点,正是Body部分。

到这里,基本上就破案例~

那是不是意味着,初始化的时候只要列表不为空,就不会有问题呢?我们来试试,默认传递[{}]

还真是这样,逛了一下网上的确有这种做法。

这个问题的原因是频繁改变节点的样式,导致监听器出问题,如果真是这样的话,那是不是对监听器回调进行拦截,通过防抖或者用requestAnimationFrame包裹也可以解决这个问题?比如这样:

js 复制代码
const divElem = document.querySelector("body > div");
const debounce = () => {}

const resizeObserver = new ResizeObserver(debounce(entries) => {
  for (const entry of entries) {
    entry.target.style.width = entry.contentBoxSize[0].inlineSize + 10 + "px";
  }
});

window.addEventListener("error", function (e) {
  console.error(e.message);
});

或者这样:

js 复制代码
import ResizeObserver from 'resize-observer-polyfill';

const OriginResizeObserver = ResizeObserver;
const ResizeObserver = class ResizeObserver extends OriginResizeObserver {
  constructor(callback) {
    super((entries, observer) => {
      requestAnimationFrame(() => {
        callback(entries, observer);
      });
    });
  }
};

不过我的测试结果是不行,这里我没搞懂原因,有知道的大神可以指点一下~

总结

这个错误可以通过赋值默认dataSource[{}]来解决,实际上,如果你的系统有监控系统,其实是可以忽略过滤掉这个错误的,它并不会对影响实际的渲染。

重新梳理一遍流程:

js 复制代码
renderWithHooks -> Component -> Table -> Body -> data.length -> ExpandedRow -> style width

renderWithHooks -> Component -> Table -> ResizeObserver -> SingleObserver -> ResizeObserverPolyfill

可以看到,通过一步一步调试技巧,可以很好排查第三方库源码问题,而对于自身业务出现的错误,那就更加简单了。

相关推荐
喝拿铁写前端30 分钟前
前端开发者使用 AI 的能力层级——从表面使用到工程化能力的真正分水岭
前端·人工智能·程序员
wuhen_n1 小时前
LeetCode -- 15. 三数之和(中等)
前端·javascript·算法·leetcode
七月shi人1 小时前
AI浪潮下,前端路在何方
前端·人工智能·ai编程
非凡ghost1 小时前
MusicPlayer2(本地音乐播放器)
前端·windows·学习·软件需求
脾气有点小暴1 小时前
scroll-view分页加载
前端·javascript·uni-app
beckyye2 小时前
ant design vue Table根据数据合并单元格
前端·antd
布列瑟农的星空2 小时前
还在手动翻译国际化词条?AST解析+AI翻译实现一键替换
前端·后端·ai编程
土豆12502 小时前
Rust 错误处理完全指南:从入门到精通
前端·rust·编程语言
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之mmove命令(实操篇)
linux·服务器·前端·chrome·笔记