Ant Design V4 升级之旅

注:本篇旨在记录并回顾团队项目进行Ant Design V4版本升级的全过程,及过程中所遇到的一些问题,不涉及深入的技术分析,以期为面对类似情况的开发者提供一些思路及参考。

今年(2020年)年初,Ant Design发布了4.0大版本更新。新的V4版本带来了许多值得关注的优化及改变,如升级调整后的设计规范、全局范围的性能优化、顺应潮流引入的暗黑主题等等。经过社区半年多的实践反馈,也该是时候让我们的项目踏上V4升级之旅了。

开始之前

在决定实施升级之前,我们需要先(针对我们团队的前端项目)解答以下两个问题:

为什么要升级?

  • Ant Design仍然是我们团队的前端项目(以下简称"项目")中使用最广泛的UI库;

  • V4版本对项目目前经常使用的一些组件进行了重构,修复了遗留问题,并带来了性能优化;

  • V4版本提供了一些新的功能,这些功能对我们来说很有引入的价值;

  • V4版本开始使用React Hooks来重写部分组件,这与项目目前的代码风格趋势一致。

为什么现在升级?

  • 项目一直使用在使用V3版本,但社区对V3的维护已于2020年6月停止,继续使用一个废弃的版本,风险不言而喻;

  • V4发布的时间已经足够长,大部分的基础问题都已得到解决,不需要担心踩坑的危险;

  • 团队计划对项目网站的UI风格进行统一和优化,先进行升级便于协调后续安排。

变更要点一览

关于Ant Design V4的升级详情,大家可以参考官网文档,以下是部分要点概括,便于大家了解概况:

  • 设计规范调整:包括基础风格和样式的调整,以及新的暗色主题效果、无边框风格组件;

  • 兼容性调整:涉及对IE的兼容性调整,更需要关注的是V4版本更多的使用了Hooks来简化代码;

  • 移除废弃的API:移除了一些在V3版本中提示Warning信息的废弃API;

  • 图标升级:通过引入SVG图标,减少了antd的默认包大小;

  • 组件重构:主要包括对FormDate Picker组件的重写,以及新的TreeSelectTreeSelectAutoComplete组件,同时,引入虚拟滚动,优化了承载大量选项/项目组件的渲染性能。

准备升级

为了使升级尽可能的简单平滑,Ant Design V4已尽可能保证了最大程度的兼容性,但仍有一些问题需要特别处理,其中涉及到一些准备工作:

  • 升级antd版本:需要先将antd的版本升级到3.26.18(3.x的最新版本),并根据控制台的Warning信息删除/修改相关API。

  • 确认React版本:文档建议的版本是16.12.0以上,而项目目前使用的版本是16.9.0,理论上需要升级,但考虑到antd仍能够支持16.9.0版本,为了减少升级的工作量,避免引入更多的风险,我们决定暂不升级React。

开始升级

核心思路

首先,我们尝试使用Ant Design团队提供的codemod cli工具(@ant-design/codemod-v4)进行迁移,其可以帮助我们解决大多数基础问题。

通过运行命令:

npx -p @ant-design/codemod-v4 antd4-codemod src

工具可以自动完成部分迁移,并对不能自动迁移的部分进行提示说明。

而后,根据提示逐一手动迁移不能自动完成的部分,待这些提示部分手动迁移完成后,再次执行上述命令重新确认。

如此循环往复,直到完成所有的迁移工作。

升级详情

以下为项目在升级过程中涉及到的一些代码变更示例:

  • Icon

@ant-design/icons中引入特定icon

-  import  { Icon }  from  'antd'  
+  import  { ExclamationCircleFilled }  from  '@ant-design/icons'  

-  <Icon type="exclamation-circle" theme="filled"  />  
+  <ExclamationCircleFilled />

@ant-design/icons中引入Icon组件,以构建自定义图标

-  import  { Icon }  from  'antd'  
+  import Icon from  '@ant-design/icons'  <Icon component={svg}  />
  • Select

对于Select组件, filterOption第二个参数直接返回原数据,不在需要通过option.props.children来进行匹配。 Optionvalue属性改为必传参数。

<Select
     showSearch
     style={{ width:  200  }} 
     placeholder="Select a person"
     optionFilterProp="children"
     onChange={onChange}
     onFocus={onFocus}
     onBlur={onBlur}
     onSearch={onSearch}
     filterOption={(input, option)  =>  
-    option.props.children 
+    option.children
          .toLowerCase()
          .indexOf(input.toLowerCase())  >=  0
     }
>
     <Option 
+       value="jack"
     > 
        Jack
     </Option>  
</Select>
  • AutoComplete

AutoComplete选项与Select一致,需要使用options代替dataSource

<AutoComplete 
-   dataSource={this.state.loadOptions} 
+   options={this.state.loadOptions}
    onSelect={this.onSelect}
    onChange={this.triggerSearch}
    optionLabelProp={'value'}
    placeholder="Search by Name" 
/> 
  • Pagination

Pagination4.1.0版本起,会默认将showSizeChanger参数设置为true ,因而在数据条数超过50时,pageSize切换器会默认显示。这个变化同样适用于Table组件。

<Pagination 
+   showSizeChanger={false}  
/>

类名变化

通常来说,开发工作并不需要太关注类名变化,但由于我们的Cypress Testing脚本在定位DOM节点时大量使用了antd类名,对于一些重构后的组件,我们详细对比了更改前后的类名变化,此处不再赘述。

样式变化

由于项目前期在使用antd组件时,或多或少地使用了:global()对默认样式进行了重写,导致部分页面同类型组件却出现了不同样式,为了便于升级时调试,也为了之后能够进行进一步的全站样式统一优化,我们去掉了大部分的样式重写,与antd的默认样式保持一致。

主要涉及:InputDropdownSelectCalendarTimePickerDatePicker

• height: 42px、44.8px、48px -> 40px
• font-size: 14px -> 16px
• margin-bottom: 15px、16px、30px -> 16px

问题回顾

ESlint

  • moment:在升级antd之后,我们发现部分类型报错,经查确认是moment版本问题导致,最低支持的版本为2.25.3
// Type 'Moment | undefined' is not assignable to type 'Moment | null | undefined'. // Type 'Moment' is missing the following properties from type 'Moment': isoWeeksInISOWeekYear, tz
  • @types/react、@types/react-dom:同时我们也发现很多难以定位的类型问题,在尝试升级@types/react@types/react-dom后,问题得到解决。
src/lib/components/Icons/index.tsx:147:8 
    -error TS2741: Property 'translate' is missing in type '{children?: ReactNode; color?: string | undefined; component: FC<{}>; className: string;}' but required in type 'Pick<IconComponentProps, "max" | "required" | ... 350 more ... | “onTransitionEndCapture">'. 
147 <Icon...

本地无法启动

升级后,项目在本地开发环境无法启动,控制台出现报错:

经常确认是升级moment版本导致,修改webpack.config.js配置,移除moment.js设置后问题解决。

module:  {  
//  noParse: /moment\.js/,  
}

功能失效

PopoverTooltip组件在升级后报类型错误,经查确认为antd bug,于是在Github创建了issue,该问题在antd最新的release已经得到修复。

<Popover
  trigger="hover"
  placement="bottomLeft" 
>
  <WarningOutlined className={warningIconClass} />
  <WarningOutlined className={warningIconClass} />
</Popover>  

// This JSX tag's 'children' prop expects a single child of type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | undefined', but multiple children were provided.

单元测试失败

目前,我们的项目主要使用react-testing-library编写进行单元测试,在升级后,部分UT失败,经查主要是以下两个原因导致:

  • 类名改变,选择器失效无法定位元素,导致断言失败;(修改选择器修复)

  • 由于因加入的虚拟滚动功能,Select, Dropdown, AutoComplete 等组件在获取下拉菜单选项时,无法获取当前未在屏幕上显示的元素,导致断言失败;(修改逻辑,先输入操作保证元素显示后再获取元素,修复)

+function doSelectDropdownOption( 
+  labelText: string, 
+  selectOption: string 
+) { 
+  const selectLabel = getByLabelText(labelText) 
+  fireEvent.click(selectLabel) 
+  act(() => { 
+    fireEvent.input( 
+      selectLabel.parentElement?.querySelector('input')!, 
+      { 
+        target: { value: selectOption }, 
+      } 
+    ) 
+  }) 
+  fireEvent.click(getDocTypeMenuItem(selectOption)!) 
+}  

it('test', () => {
  fireEvent.click(getByText('Upload Document'))
  const uploadBtn = getByText('Upload')
  // The upload button should be disabled until we have chosen a load
  expect(uploadBtn).toBeDisabled()
  doSelectFile()
  // Select a document type 
+ doSelectDropdownOption('Document Type', 'Other (Load)')
  expect(uploadBtn).toBeDisabled()
})

升级完成

至此,升级工作就算是告一段落了,但对我们来说,后续要做的事情还有很多,如升级React到推荐版本16.12.0、规范并统一组件风格样式、完善组件库/文档等等。整个升级过程虽然不存在太高的技术难度,但却是一份非常费时费力的工作,我们不得不踩入团队自己挖下的坑,因此,为了避免重蹈覆辙,我们在代码规范上又加上了两条:

  • 保持对第三方依赖库的规律性升级;

  • 保证项目中页面的自定义样式尽可能一致。

这也算是另一个收获了。

“卓派前端工作志,聚焦实用前端技术,让编程更有趣!”

前端技术组 @ 西安卓派科技 NEXT Trucking — 拉勾 | Boss | 知乎 | 掘金 | 简书

如果觉得本文对你有帮助的话,快来关注我们吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351