antd pro 官方文档介绍了动态切换主题的方法。但是并没有讲的特别清楚,而且编译主题的时间比较长,非常影响开发效率。下面介绍下我的解决办法。
# 自定义主题样式
# 动态主题切换原理
主题切换主要是通过 unm-plugin-antd-theme 插件实现的,unm-plugin-antd-theme 插件通过 antd-pro-merge-less 将 less 文件编译为一个主题 css 文件。
所以 unm-plugin-antd-theme 的配置文件就是定义的 antd-pro-merge-less (https://github.com/chenshuai2144/antd-pro-merge-less) 的参数,如下所示,要求是 json 文件,文件名 theme.config.json:
1 | { |
每种主题一个不同的文件,fileName 定义了文件名,theme 为 dark 表示暗黑主题(对应 antdpro 主题设置里面的 realDark),不写就是亮色主题(对应 antdpro 主题设置里面的 light 或 dark)。modifyVars 就是对 antd 默认变量的定制。
如果发现上面没有默认的亮色主题,因为这时不用单独的主题 css 文件,直接用默认的就好。
每次编译的时候 unm-plugin-antd-theme 插件会遍历上面定义的每个主题,编译为一个 css 文件,开发时位于 node_modules/.plugin-theme/theme 目录下,发布时位于 theme 目录下,每个主题文件编译都需要几秒钟,所以主题多了编译时间会比较慢,后面会讲怎么解决这个问题。
关于插件的使用,umi3 对于 umi-plugin 开头的插件是自动加载的,不需定义在 config 文件的 plugins 中,如果看到一些老的教程不要感到困惑。
好了,有了主题文件,接下来要解决怎么应用主题,其实很简单,就是重新加载新的 css 文件。antd pro 官方给了参考例子,也可以参考 SettingDrawer 源代码。
# 解决主题编译慢问题
开发期每次都编译主题是难以忍受的,所以我的办法是开发器能够配置是否编译主题,而发布时总是编译主题。可惜 unm-plugin-antd-theme 插件本身并没有提供这种配置能力。好在 umi 的扩展能力非常强,我写了一个插件来解决这个问题,其实非常简单,代码只有几行,插件名 umi-plugin-config。
也可以使用 umi 禁用插件的方法, unm-plugin-antd-theme 插件插件的 key 为 antdTheme,修改 config 文件如下:
1 | import { defineConfig } from 'umi'; |
# 主题配件文件优化
unm-plugin-antd-theme 插件的配置文件为 json 格式,很不方便,一来不好加入注释,二来不好共享一些变量,毕竟多个主题有一些一样的成分。为了解决这个问题,还是用到 umi 插件的扩展能力,在插件中将 js 定义转换为 json 定义,详见上述 umi-plugin-config 插件。
通过例子说明这样做的好处。你是否还记得默认的亮色主题没有编译为单独的主题 css,那么所有主题统一修改的变量如果做呢。
默认的主题还是可以通过 umi 的配置文件的 theme 配置:
1 | export default { |
1 | import { defineConfig } from 'umi'; |
然后其他主题就需要在 unm-plugin-antd-theme 插件的 json 配置文件中,每个主题的 modifyVars 中都要加入,本文一开始已经给出了例子。
显而易见上面的统一变量修改需要烦人的重复,将 json 改成 js 后就能完美解决这个问题,theme.config.json 会变成如下,需要使用 CommonJS 规范定义模块:
1 | const theme = require('./theme').default; |
这样就可以将统一的变量修改复用起来。umi-plugin-config 插件检测到 theme.config.js 文件就会自动转换为 theme.config.json 文件。
# 自定义主题注意事项
要支持动态切换主题,需要严格规范样式的定义方式。
- 不要使用固定的颜色,尽量使用 antd 已有变量,例如 @text-color,表示文字颜色,在亮色和暗黑模式下值是不一样的,antd-pro-merge-less 已经为我们自动处理了,我们只需要使用 @text-color
- 所有主题统一修改的变量定义在上述 theme.js 中,所有主题复用
- 主题不一样的变量修改在各自的配置中定义
- 所有主题统一修改的样式定义在 global.less 文件中,需要设置最高优先级
- 所有主题用的同一个变量但是值不一样(亮色和暗黑不一样),定义在组件 less 中,需要使用:global
- 如果无法通过 less 定义样式,可以通过 settings 获取主题后动态设置行内样式