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

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

相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai2 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9152 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
小牛itbull4 小时前
ReactPress:重塑内容管理的未来
react.js·github·reactpress