antd@4升级

Posted by wangtiegang on March 28, 2020

在看到 antd@4 发布后,我立马就想升级了,因为它提供了更好的性能,select ,table 和 tree 已经全面支持了虚拟滚动,这个诱惑实在是太大了,毕竟要我自己解决性能问题,实在是有点难。于是马上跟随官方教程升级,升级过程很简单,但是我也碰到了一些问题,一并记录下来。

首先安装 codemod-v4

1
2
# 通过 npx 直接运行
npx -p @ant-design/codemod-v4 antd4-codemod src

codemod-v4 会自动帮我们转化一些代码,比如 <Icon type={type} /> 会转化成 LegacyIcon ,但是转化完之后,需要自己把 LegacyIcon 改为需要的 icon 组件,如果不改,会导致项目打包文件更大。antd@4 对 form 进行了重写,原来的 getFieldDecoratorForm.create 没有了,第一个直接删除就好,第二个可以改为 Form.useForm ,如果还有其他报错的代码,需要视情况进行修改。

安装插件让 pro 的路由兼容 icon 配置

在升级之前,pro 项目的菜单 icon 是在路由中以 icon: string 形式配置的,但是 antd@4 中不再支持这种方式了,可以安装官方的插件 umi-plugin-antd-icon-config 来兼容。

1
2
# 安装
yarn add umi-plugin-antd-icon-config -D

在 config.ts 中设置

1
2
3
export default {
  plugins: [['umi-plugin-antd-icon-config', {}]],
};

如果前面没有什么问题的话,整个升级过程就完成了,然后启动项目,发现不行,我的菜单 icon 不见了。。。

这就很懵逼了,难道我操作出了问题吗?整个过程这么简单,没理由啊。于是我先下载了 antd@4 版本的 ant desgin pro,进行 package 对比,发现有一些依赖包存在差异,不管原因,先一起升了再说,然后发现还是不行,这个时候就不能从自己的项目上找原因了。进一步发现 umi-plugin-antd-icon-config 在 github 上存在仓库,于是进去查看 issues ,立马发现一个相关: 从服务器请求菜单(或指定menuData中的icon为字符串)时插件无效,巧了,我的菜单也是从服务端获取的,进去看了一下确定是同一个问题,知道了原因,因为 antd@4 不支持字符串方式的 icon,所以需要插件将字符串转换成对于的 icon dom 结构,但是插件只直接根据路由中的配置进行转换,如果是服务端获取的菜单,无法转换。而且作者说可以自己定义一个枚举进行映射,没直接给出解决方式。这真的有点难了,我前端真的很菜的啊,没有马上理解是个什么意思,最后不得不给提出 issue 的人留言,问问他是怎么解决的,最后知道了答案,他在插件处理路由配置的结果上,中途拦截生成的菜单结构,跟服务端获取的菜单进行对比,只取有权限的部分。看到这个解决方法之后,我瞬间理解了作者关于使用枚举的方式,于是自己实现了一个,代码如下:

首先定义一个枚举,包含需要的菜单icon

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import { SmileOutlined, HomeOutlined, PicLeftOutlined, SettingOutlined } from '@ant-design/icons';

const iconEnum = {
  smile: <SmileOutlined />,
  home: <HomeOutlined />,
  picLeft: <PicLeftOutlined />,
  setting: <SettingOutlined />,
};

export default iconEnum;

BasicLaout.jsx 中定义一个递归映射函数,在获取到服务端的菜单数据后,对其进行映射转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ... 省略其他 import
import iconEnum from './iconEnum';

// 将服务端获取的菜单 icon 字符串映射为对应的 icon Dom
const mappingIcon = menuData => {
  const mappingMenu = menuData.map(item => ({
    ...item,
    icon: iconEnum[item.icon],
    children: item.children ? mappingIcon(item.children) : [],
  }));
  return mappingMenu;
};

// menuData 为服务端获取的菜单数据
const iconMenuData = mappingIcon(menuData);

// ProLayout
<ProLayout menuDataRender={() => iconMenuData} />

关于 pro-table

在升级的过程中,发现了 pro-table 这个组件,很酷炫美观,于是马上安装使用,但是结果并不是很满意。

pro-table 可以根据列自动生成搜查表单,并且也可以自定义,而且表格部分多了固定列,隐藏列,全屏等功能,进一步封装了获取数据的方式,可以很快的开发一个功能强大又美观的表格,但是结果性能很差,不知道是我写的有问题还是本身有性能问题。首先我的表格列非常多,接近40列,数据一次展示50行,搜索表单项也达到十几个,在 pro-table 下,第一次加载时间很长,翻页非常卡顿,到了无法使用的地步,我测试了一下,如果我把列减少到个位数,则跟官网一样流畅,于是我又在 antd@4 的基础上自己实现一个类似的搜索表单,发现性能好很多,基本感觉不到卡顿,所以只能放弃全屏固定列等功能了。

在网上查询了下长列表的问题,通常行数很多的话,需要采用虚拟滚动才能提升性能,antd@4 已经直接支持,这个真的是帮了大忙,但是如果列很多,好像并没有支持横向虚拟滚动。不过很多人认为不应该在表格中展示很多列,应该通过二级页面去展示,我觉得也有道理,很多时候展示列很多,用户体验不好,未必会去看,不如展示一些核心列,真需要查看全部,可以点进去查看详情。在财务系统的开发过程中,有些表格达到恐怖的100+列,我也不知道他们是不是真的需要,更别提他们还需要单元格编辑了,这真的是噩梦,在 jquery 时代下还没什么问题,在 react 下不知会怎么样,等真到了需要改造的时候希望能有更高性能的 table 组件能支持。