- 系统采用了qiankun 微服务框架,主应用换肤后需要传递给每一个子应用进行换肤的功能;
- 同时我们采用了组件库二次封装(hs-ui),针对组件库也需要做换肤功能
方案一:
暗黑模式,通过css filter滤镜,进行页面颜色反转处理,需要对反转的地方添加处理,图片则需要反转回来,但是针对自定义颜色难以控制
@media (prefers-color-scheme: dark) {
filter: invert(1) hue-rotate(180deg);
}
方案二:
使用 css3 的 Variables(需要考虑浏览器支持情况)重新整理源码中的 less变量,在线修改 css 变量达到切换效果,但是组件库中使用了很多的less的颜色函数还只是预处理能力不支持 css 变量编译的,需要做很多的组件样式覆盖处理,这是需要不少的工作量的;
ant-desing-vue需要4.17以上版本
方案三:
预设多份 less 变量文件,使用 webpack构建能力提前将所有的样式(包括组件库的)编译出总的多份 css 文件,在线切换 css 文件到达目的,但是需要对项目的所有的 less 的引用模式作调整,对构建环境也需很大的调整,样式与 js 完全分离,如果有使用 css modules 是更大的麻烦,而且在开发模式下修改调试样式极其不友好,还不能友好地对组件库的的 less 按需编译;
方案四:
如果使用的是 ant-design,可以选择采用antd-theme-webpack-plugin、antd-theme-generator、umi-plugin-antd-theme等, less.modifyVars实现换肤,针对于颜色的定制,这也仅限于 antd;
方案五:
使用webpack-theme-color-replacer(vite版本对应的是vite-plugin-theme),这个插件可以从所有输出的css文件(如element-ui theme colors)中提取主题颜色样式,并生成一个只包含颜色样式的“theme-colors.css”文件。在你的网页运行时,客户端部分将帮助你下载这个css文件,然后动态地将颜色替换为新的自定义颜色。
方案四的实践
步骤一:安装
yarn add antd-theme-generator@1.2.11 --save
步骤二:增加主题文件
在config配置目录下创建dark.js,文件示例如下:
const dark = {
"@primary-color": '#00AEFC',
"@menu-dark-bg": '#212226',
"@link-color": '#00AEFC',
"@custom-bg": '#00AEFC',
"@mainBgColor": '#363940',
"@layout-body-background": '#212226',
"@layout-header-background": '#212226',
"@nav-bg-color": '#43464D',
"@nav-tab-active-color": '#363940'
}
module.exports = {
dark
}
步骤三:配置 color.js
安装完成在根目录下创建color.js文件, 文件配置如下:
const { generateTheme } = require("antd-theme-generator");
const { dark } = require('./src/config/dark') // 引入方式不支持import
const fs = require("fs");
const path = require("path");
//生成的theme.less文件的位置
const outputFilePath = path.join(__dirname, "./public/theme.less");
//自定义样式
const cusCssFilePath = path.join(__dirname, "./src/assets/css/index.less");
// 主题变量
const themeVariables = Object.keys(dark)
const options = {
antDir: path.join(__dirname, "./node_modules/ant-design-vue"), //antd包位置
stylesDir: path.join(__dirname, "./src/assets/css"), //主题文件所在文件夹
varFile: path.join(__dirname, "./src/assets/css/variables.less"), // 自定义默认的主题色
mainLessFile: cusCssFilePath, // 项目中其他自定义的样式(如果不需要动态修改其他样式,该文件可以为空)
themeVariables: themeVariables, //要改变的主题变量
outputFilePath: outputFilePath, // 是否只生成一次
customColorRegexArray: [/^color\(.*\)$/],
};
generateTheme(options)
.then((less) => {
//自定义样式与ant主题样式合并
//读取提取过的ant样式
const themeCss = fs.readFileSync(outputFilePath).toString();
//读取自定义的CSS
const cusCss = fs.readFileSync(cusCssFilePath).toString();
fs.writeFileSync(outputFilePath, themeCss + cusCss);
//重新覆盖themeCss
console.log(`🌈 主题覆盖成功. OutputFile: ${outputFilePath}`);
})
.catch((error) => {
console.log("Error", error);
});
步骤四:运行 color.js
可以在vue.config.js获取
require('./color')
添加完配置,/src/assets/ 创建一个 theme 文件夹,文件夹下创建文件 variables.less 和 index.less文件
index.less文件内容可以为空,variables.less 文件内容:
// 这段样式引入必须添加
@import "~ant-design-vue/lib/style/themes/default.less";
@primary-color: #992777;
复制代码
文件创建完毕,执行启动开发环境命令 yarn run serve,打包成功在public文件夹会有一个,theme.less 文件,如图:
步骤五:index.html 写入
<body>
...
<link rel="stylesheet/less" type="text/css" href="./theme.less" />
<div id="hsMainApp"></div>
// 需要使用到window.less这个对象里的方法
<script src="https://cdn.bootcss.com/less.js/2.7.3/less.min.js"></script>
...
</body>
使用
declare const window: Window & { less: any, }; // 防止报没有less属性
window.less.modifyVars({
'@primary-color': 'red' // 可根据当前主题,切换对应变量即可
})
.then(() => {
console.log('成功');
})
.catch((error: any) => {
alert('失败');
console.log(error);
});
qiankun使用
- 当使用了
样式隔离{ strictStyleIsolation: true },由子服务自己更新主题 未使用样式隔离,则不需要下面这步, 统一由主服务更新主题, 样式层级需求高点,避免被子服务覆盖。
/* 主服务 */
// 通知子应用换肤
const { setGlobalState } = initGlobalState()
setGlobalState({ theme: targetTheme })
/* 子服务 */
// 接收主服务传递过来的参数
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value: any, prev: any) => {
console.log('子应用接收参数')
setTheme(value)
console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev)
},
true
);
注意事项
1. antd-theme-generator版本问题
推荐使用: yarn add antd-theme-generator@1.2.11 --save
2. 样式覆盖,样式不出现的问题
- 注意theme.less的 link标签放的位置,要放在body的第一行里,因为到时候style是会生成在该ling标签下面的,如果你把link放在head里,到时候生成的主题样式会被覆盖掉。
- 我们需要在theme.less 的link 的下方引入cdn.bootcss.com/less.js/2.7… (less版本不可以超过3.0)文件,因为我们需要使用到window.less这个对象里的方法,但是我们不能引入less3.0以上的,不然浏览器控制台会报错,样式也不会出现。
总结
有更好、简单的方案,请在评论区艾特我,谢谢!
参考链接
基于less和sass的在webpack或vite中的动态主题的实现方案
antd-theme-generator:github.com/mzohaibqc/a…
antd-theme-webpack-plugin: github.com/mzohaibqc/a…