likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 17

前言

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

通过Vs Code调试,我找到了Ant Design Table运行报错问题~ 那太简单了,给antd table加上scroll属性不就行了?并且给指定Column加上width,于是:

<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>

但运行之后给我报了这个错,antd版本是4.20 通过Vs Code调试,我找到了Ant Design Table运行报错问题~

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

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

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

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

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

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

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

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

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

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

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

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

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

Vs Code调试

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

{
  // 使用 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中打个断点试试:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

还真是这样,逛了一下网上的确有这种做法。 通过Vs Code调试,我找到了Ant Design Table运行报错问题~

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

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);
});

或者这样:

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[{}]来解决,实际上,如果你的系统有监控系统,其实是可以忽略过滤掉这个错误的,它并不会影响实际的渲染效果。

重新梳理一遍流程:


renderWithHooks -> Component -> Table -> Body -> data.length -> ExpandedRow -> style width

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

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