头图

antd v5版本发布至今已有整整4个月了,在v5版本发布后我也是第一时间把之前v4项目迁移到了v5,对于此次升级还是有很大的变化跟改进的,故在此浅聊一下我的迁移经历和建议,前提是你已经通读过官方的迁移指南

重大改进

除了 ui 风格变化、更好的支持 Typescript、优化底层库的依赖等,我觉得此次大版本更新带来最大的变化是 CSS-in-JS 替代原本 less 的技术方案变更。升至v5后 antd 产物不再导出 css 文件,只有一个 reset.css ,况且这个重置样式文件也只是为了让开发者不再去依赖其他的诸如 Normalize.css 的第三方库。
image-20230319144130194

为什么说这是一次重大改进?做过 antd 切换主题功能的开发者应该深有体会,我在之前的文章也有介绍过实现方案:[前端实现切换主题方案](https://segmentfault.com/a/1190000042573976) 。流程是先提前编译出来每种模式下的对应 less 文件,然后通过包裹 antd 提供的 ConfigProvider ,根据 mode 的变幻动态传值修改 prefixCls 的值以动态修改组件内的样式,这样做有以下几种弊端:

  1. 每加一种主题就要编译一套less,尽管有很多重复的样式代码。
  2. 使得项目打包后的体积变得越来越臃肿。
  3. 维护起来非常费劲,因为每改一个 antd 组件的通用样式就要同时去修改每套主题下的 less 文件对应的相同代码,尽管他们只有 prefixCls 不同。
  4. 修改样式要格外注重样式权重问题,加重了开发者的心智负担。
  5. 修改主题可能会花费更多的时间和写更多的代码,因为开发者需要去找到修改样式的元素的dom层级,然后在样式文件中为了权重问题需要写 full path
  6. ...(我没经历过的其他问题)

而采用 CSS-in-JS 这样的技术方案则会大大改善以上问题的出现:

  1. 首先它是一种将 css 代码写成 JavaScript 对象或函数的方法,因此可以通过使用变量、函数等动态生成样式,方便响应式设计。
  2. 并且相比 less 它作用域控制更容易,因此很好的解决了命名冲突和样式权重的问题。
  3. 在一定程度上他解决了一部分性能优化的问题,因为减少了请求 css 样式文件的 http 请求。

迁移指南

如果项目中没有除了黑夜\白天模式以外的其他主题,并且没有适配 IE 浏览器的需求,那么恭喜你,迁移此项目的成本将是非常轻量级的,直接 yarn add antd 升级库版本,移除 v4 项目中引用到的 antd 内置的文件(antd/dist/antd.css),之前在 less 文件中通过 :global 去修改 antd 组件默认样式的行为同样会生效,如果之前做了主题切换功能的项目同样也可以把之前的主题样式文件删了,antd 内置了白天(default)、黑夜模式和紧凑模式的算法(algorithm)。这个 algorithm 在体现上来看可以理解为风格或者主题吧。那如果要修改单个组件的样式,比如antd 按钮默认高度为32,在某个业务场景下,这个按钮需要改成40,那么可以修改主题中的 token 去实现这一目的而不用去修改样式文件,这个 token 怎么理解?可以看成是组成一个组件的每个颗粒化的样式,也就是一些样式变量名而已。

那么,为什么修改 、token 的方案会比之前修改样式文件的方案要更优呢?

举个简单的例子,我要修改 Select 的高度,以前的做法是修改它的样式,但是理想中只要修改 height 属性就行了,但是往往不是,他还有 line-height 等其他样式会影响到布局,所以在过去,我们要修改这类组件的样式,虽然仅仅是想改变高度,但是还是不得已去改变其他肯能会影响到布局的样式,比如:

<Select
  className={styles.input}
  style={{ width: 200 }}
  placeholder="请选择...."
/>
.input {
  height: 40px;
}

效果如下,可以明显看到input的高度没变,只是改了外层container的高度,并且图标也不居中了。

image-20230319153415312

image-20230319153437506

再看一下再v5版本中如何实现改变高度:

<ConfigProvider
  theme={{
    token: {
      controlHeight: 40
    }
  }}
>
  {/* container 只是为了让 Select 居中显示 */}
  <div className={styles.container}>
    <Select
      className={styles.input}
      style={{ width: 200 }}
      placeholder="请选择...."
    />
  </div>
</ConfigProvider>

效果如下,而我只是修改了一个变量值而已,其他的可能会导致布局的问题我一概不用去管。

image-20230319153725566

如何实现主题切换功能

如果你只是需要实现白天黑夜模式的切换,那非常简单,因为 antd 内置了白天(默认)和黑夜模式的变量(算法),只要在项目中根据你的 theme mode 动态传递给 ConfigProvider 就行了,如果是全局生效,你只要在你的入口文件(jsx 或者 tsx)中配置即可:

<ConfigProvider
  theme={{
    algorithm:
      theme === 'dark'
        ? antdTheme.darkAlgorithm
        : antdTheme.defaultAlgorithm
  }}
>
  <div className={styles.container}>
    <Select
      className={styles.input}
      style={{ width: 200 }}
      placeholder="请输入...."
    />
  </div>
</ConfigProvider>

如下就是 dark 模式下的 Select 组件样式:

image-20230319154913216

ConfigProvider 在v4版本中我只会在需要修改一些全局配置的时候会用到,比如说国际化、配置 prefixCls 等,但是在v5中我会频繁使用到,因为经常要用它来修改一些 antd 组件的局部样式,通过修改 token 变量值的方法,比如以下:

<ConfigProvider
  theme={{
    algorithm:
      theme === 'dark'
        ? antdTheme.darkAlgorithm
        : antdTheme.defaultAlgorithm
  }}
>
  <div className={styles.container}>
    <Select
      className={styles.input}
      style={{ width: 200 }}
      placeholder="请输入...."
    />
    <ConfigProvider
      theme={{
        components: {
          Button: { controlHeight: 26 }
        }
      }}
    >
      <Button>按钮</Button>
    </ConfigProvider>
  </div>
</ConfigProvider>

那如何实现更多的自定义主题呢?其实思路跟v4是一样的,修改样式变量值,只不过在v5中是通过修改js变量,而在以前的版本是修改 less 变量,具体实现方法可以参考 antd v5 版本中 default(对应源码 components/theme/themes/default(或者dark)),本质上是覆写变量值,只是变量名需要一一对应起来。

开发时使用技巧

使用v5版本这么长时间以来,除了在 nextjs 项目中最初版本会有水合问题以外,最大的问题就是你想去修改某个组件的某个局部样式,但是不知道对应的变量名,我的建议是 clone 一份 antd 最新版本的源码,在不知道变量名(token)的时候去源码中 components 目录下找到你要的组件,然后找到其下的 style 目录,找到对应样式下它引用的 token 属性名,以 Button 组件的背景色为例:

image-20230319160829808

那么,修改 colorBgContainer 就ok了:

<ConfigProvider
  theme={{
    token: {
      colorPrimary: '#FF9B50', // 主题色
      colorBgContainer: '#FF9B50' // 写在这里跟写在下面都一样
    },
    components: {
      Button: { controlHeight: 50, colorBgContainer: '#FF9B50' }
    }
  }}
>
  <Button className={styles.btn} onClick={handleDeposit}>
    <span style={{ color: '#fff' }}>
      {isConnected ? `Deposit 3 ETH` : 'Unlock WalletConnect'}
    </span>
  </Button>
</ConfigProvider>

如果你只是针对某个业务场景下的某个组件修改局部样式,建议写在 components 下,如果是某个模块中通用的样式,则选择 token,本质上都是修改 token,只是划分区域更细致化而已。

使用案例

  1. Phalcon:强大的区块链交易浏览器
  2. MetaSleuth:加密货币资金可视化分析追踪工具


MangoGoing
774 声望1.2k 粉丝

开源项目:详见个人详情