1 动态主题色
效果如下:
GitHub 源码:umi-antd-dynamic-theme 1.0.0 分支
1.1 前言
本人项目使用的 ui 库为 antd,样式为 less。
主题色的变化,antd 官网提供了相关的方案:定制主题,但是,该方案是静态的换肤,也就是已经知道系统需要什么样的主题色,根据相关的配置,antd 自动帮你做转化。
我接下来要讲的动态主题色变化,也就是,你的页面可能会有10种,或者20种颜色需要切换,你也不知道到底有多少种颜色,同时,文档也考虑到多人协助开发,开发人员只需要按照约定方式去编写样式、主题文件名、目录等命名规范,是本人真实项目应用总结。
主要思路:动态插入样式,覆盖系统已经编译好的相关样式,包括 UI 组件库 和 自定义样式。
1.2 实现
步骤一:在 Umi 里配置主题
如果你在使用 Umi,那么可以很方便地在项目根目录的 .umirc.ts
或 config/config.ts 文件中 theme 字段进行主题配置。theme
可以配置为一个对象或文件路径。
"theme": {
"primary-color": "#1DA57A",
},
或者 一个 js 文件:
"theme": "./theme.js",
本人使用的是公司自己的框架,基于 Umi 进行二次封装,所以,在 本目录下的config/theme.ts
配置 @primary-color
,就实现了默认主题色的配置。
步骤二:新建相关目录和文件
在根目录下,新建public
目录,引入less.min.js
,下载路径为:https://cdn.bootcss.com/less.js/2.5.3/less.min.js,同时新建 styles
目录,再创建antd.theme.less
和 components.less
两个文件;
antd.theme.less:用来放置覆盖
antd
UI 组件库样式;components.less:引入项目各个开发人员的相关主题样式文件;
说明:为啥是 public 目录呢?因为 Umi 项目,public 目录下所有文件会被 copy 到输出路径,也就是相关的资源,是被直接放到项目根目录。
具体如下图:
步骤三:Umi 配置 copy
copy
属性主要作用是,设置要复制到输出目录的文件或文件夹。其实原理就是利用 webpack 插件 copy-webpack-plugin
,只不过 Umi,抽成了配置,如下图:
说明:配置 copy 目的,就是将各个模块自定义主题样式,复制到 public styles 目录下,因为动态变化主题色时候,是需要覆盖掉相关的颜色。
步骤四:编写 Utils 方法
下面的方法主要是动态插入 less.min.js
,动态插入自定义的相关样式,利用 less.modifyVars,传入相关动态主题色参数,改变自定义的相关样式。
根据如下代码,自定义的样式文件为/styles/components.less
,该文件是作为统一的入口,引用 copy 到 public/styles 目录下
的相关各个模块的样式 和 antd 样式;
自定义的变量有@header-bar-bg
和 @primary-color
;
function _changeTheme(themeColor: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!(window as any).less) return;
window.setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).less.modifyVars({
'@header-bar-bg': themeColor,
'@primary-color': themeColor,
// '@color-primary': themeColor,
});
}, 300);
}
let lessNodesAppended: boolean = false;
/**
* 动态更改主题色
* @param {string} themeColor 主题颜色
*/
export function onChangeTheme(themeColor: string) {
if (!lessNodesAppended) {
// 插入 less.js,和 颜色主题.less
const lessConfigNode = document.createElement('script');
const lessScriptNode = document.createElement('script');
const lessStyleNode = document.createElement('link');
lessStyleNode.setAttribute('rel', 'stylesheet/less');
lessStyleNode.setAttribute('href', '/styles/components.less'); // public 目标下
lessConfigNode.innerHTML = `
window.less = {
async: true,
env: 'development',
javascriptEnabled: true
};
`;
lessScriptNode.src = '/less.min.js';
lessScriptNode.async = true;
lessScriptNode.onload = () => {
_changeTheme(themeColor);
lessScriptNode.onload = null;
};
document.body.appendChild(lessStyleNode);
document.body.appendChild(lessConfigNode);
document.body.appendChild(lessScriptNode);
lessNodesAppended = true;
} else {
_changeTheme(themeColor);
}
}
1.3 页面组件样式编写
经过以上步骤,就已经大功告成了,接下来就可以开心编写组件和相关的自定义样式了。
约定,相关的主题颜色需要抽成 xxx.theme.less,然后,在组件引入该文件,如编写 List 组件,List.less 样式文件引入 List.theme.less,如下图:
less 样式需要使用 prefix
,不能使用 import styles from './Less.less'
这种写法,因为如果使用该写法,则样式编译会被加上 hash,动态主题色覆盖就无法覆盖了。antd 官网组件也是使用 prefix
样式写法。
接下来只需要在 config/path.config.ts
和 public/styles/components.less
引入相关的文件路径即可。
config/path.config.ts
配置组件的List.theme.less
主题色;public/styles/components.less
新增“config/path.config.ts”
配置的目标主题色路径;
1.4 解决覆盖 antd UI 库组件样式
在 public/styles/antd.theme.less
文件引入所有 antd
组件样式(谷歌吧),然后在public/styles/components.less
@import 该文件。
1.5 开发步骤
如果按照文档上面的配置,那么整个系统动态主题色就配置好了,接下来就是按照约定规范,所有的开发人员,在编写页面或者组件,只需要配置如下两个地方既可,同时,注意所有的颜色都使用变量。
config/path.config.ts
配置组件的xxx.theme.less
主题色;public/styles/components.less
新增“config/path.config.ts”
配置的目标主题色路径;
1.6 总结
theme.ts 定义主色调
'@primary-color': '#5176EA',
、辅助色等相关颜色'@color-secondary': '#6c757d'
;如果有额外的不同颜色,那就需要重新定义对应的变量;
所有页面的主题色、辅助色,都使用变量
@primary-color、@color-secondary
;相关的主题色、辅助色样式,都需要单独抽出来;
每个页面模块或者组件,都需要先新建对应的主题样式文件,规范为 xxx.theme.less;
为了解决 less 模块化问题,项目统一使用 classPrefix 方案;
所有页面和组件样式定义的变量,前缀命名需要按照一定的规范,组件前端是 c-文件名称,页面路由 p-文件名称
如果页面覆盖 antd 样式,需要加
!important
,或者在xxx.theme.less
中编写;
2 升级优化
主要是对一些问题的优化升级,方便开发人员。