前言
本文环境基于umi框架,不过实现的原理在不同框架中是通用的。
方案一:利用less.modifyVars
原理简述
通过antd-theme-generator
抽取项目中所有涉及到颜色变量的样式,整合到一个less文件,然后再html中引入这个样式文件和less来对原样式进行覆盖,再利用less在浏览器中可以修改变量的特性来实现主题色的切换。
这个方案适用于antd@4.17之前的版本,之后的版本会报错
准备工作
1.安装依赖
yarn add antd-theme-generator -D
2.引入less库
可以通过script引入,也可以下载下来放到本地目录中。cdn链接:cdn.bootcss.com/less.js/2.5…
通过脚本抽离样式
1.创建样式变量文件var.less
虽然umi配置可以直接设置主题色,但是由于antd-theme-generator
会覆盖umi打包的样式,我们需要针对antd-theme-generator
抽离的样式设置主题色。
// 根目录/src/var.less
@import '~antd/lib/style/themes/default.less';
@primary-color: #76488c;
如果你是用默认主题色可以不做这一步
2.创建脚本color.js
// 根目录/script/color.js
const { generateTheme } = require('antd-theme-generator');
const path = require('path');
const options = {
// antd的路径
antDir: path.join(__dirname, '../node_modules/antd'),
// 处理颜色相关less样式的目录
stylesDir: path.join(__dirname, '../src'),
// 样式变量文件
varFile: path.join(__dirname, '../src/var.less'),
// 需要抽离的颜色变量,默认是@primary-color,可自行添加其他的变量
themeVariables: ['@primary-color'],
// 输出路径,这里输出到public,命名为color.less,也可以自定义命名,但需要
// 和下文引入link的名字对应。
// 在umi中public的文件会复制到打包目录。
outputFilePath: path.join(__dirname, '../public/color.less'),
};
// 生成样式
generateTheme(options)
.then((less) => {
console.log('Theme generated successfully');
})
.catch((error) => {
console.log('Error', error);
});
3.在package.json
中添加脚本命令
"script": {
"color": "node ./script/color.js"
}
然后我们可以在控制台用过yarn color执行脚本,将样式文件抽离并生成到public目录下。
项目内配置
在项目入口文件执行
// app.ts
const setColor = () => {
const publicPath = '' // 项目的publicPath,没有配置的可以置空
const lessConfigNode = document.createElement('script');
const lessScriptNode = document.createElement('script');
const lessStyleNode = document.createElement('link');
lessStyleNode.setAttribute('rel', 'stylesheet/less');
lessStyleNode.setAttribute('href', publicPath + '/color.less');
lessConfigNode.innerHTML = `
less = {
async: true,
env: 'development',
};
`;
lessScriptNode.src = 'https://cdn.bootcss.com/less.js/2.5.3/less.min.js';
lessScriptNode.async = true;
// 引入顺序不能改变
document.body.appendChild(lessStyleNode);
document.body.appendChild(lessConfigNode);
document.body.appendChild(lessScriptNode);
};
export function render(oldRender) {
setColor()
oldRender()
}
至此,我们便可以在项目中动态修改主题,例如:
// 在某个组件内
<input type="color" onChange={(e)=>{
// less已经在script中引入作为全局变量
less.modifyVars({
/: e.target.value,
})
}} />
方案二:利用css variable
原理简述
css variables提供了利用js修改css变量的方式,antd@4.17版本开始官方提供了利用css variables配置主题的方式。
这个方案只适用于antd@4.17及之后的版本
引入antd.variable.min.css
// global.less
@import '~antd/dist/antd.variable.min.css'
引入这个样式文件之后不需要再引入
antd/dist/antd.min.css
通过ConfigProvider配置样式变量
import { ConfigProvider } from 'antd';
ConfigProvider.config({
theme: { primaryColor: '#25b864', },
});
至此我们就可以成功修改主题色了。
如何在非antd组件使用antd提供的css变量?
我们可以查看antd/lib/style/themes/variable.less
,这里定义了主题颜色相关的css变量,我们可以直接在其他自己写的组件中使用这些变量,例如:
// 组件
<div className='color'></div>
.color{
background: var(--ant-primary-color);
}
通过ConfigProvider修改主题色的时候也会一并改变。
不过,对于一些老项目中可能会有不少地方用了例如@primary-color
这些less变量,这时候我们肯定希望可以无痛切换到css变量的,这时候我们可以利用less-loader
来实现:
// .umirc
...
lessLoader: {
// 通过globalVars在每个less文件头引入antd定义的variable.less文件,里面有less变量和css变量
// 相关的映射
globalVars: {
theme: 'true;@import "~antd/lib/style/themes/variable.less"',
}
}
...
注意:做了这个配置之后不要再配置umi的
theme
,否则会不生效,通过ConfigProvider
去配置初始的主题色
对于非umi框架也可以通过自行调用
less-loader
去实现,不过要注意less-loader
不同版本的用法,现在最新版的好像已经没有globarVars
,改为additionaldatal
了,本人也还没有测试过,各位自行测试。
开启dynamicImport导致antd默认样式覆盖的问题处理
这种情况我们可以通过splitchunk抽离antd样式和普通样式,让antd样式先于普通样式引入:
// .umirc
...
chainWebpack(config) {
config.optimization.splitChunks({
cacheGroups: {
common: {
name: 'common',
test: /\.(css|less)$/,
chunks: 'async',
minChunks: 1,
minSize: 0,
priority: 10,
reuseExistingChunk: true,
},
antdVendor: {
test: /[\\/]node_modules[\\/](antd)[\\/]/,
name: 'antdVendor',
priority: 20,
},
},
});
},
chunks: ['antdVendor', 'common', 'umi'],
...
总结
对比两种方案:
优点 | 缺点 | |
---|---|---|
方案一 | 适用于范围较广,可用于4.17之前的版本 | 配置较复杂,antd-theme-generator较长时间没有更新,对于较新的版本不再支持 |
方案二 | 配置简单,使用方便 | 不支持旧版本,目前来说也是实验性方案,不知未来会怎样变化 |
总的来说,个人推荐是尽量用方案二,实在不行再用方案一。
最后,如果觉得文章有帮助的话,别忘了点个赞哦!