这是我的第一篇博客,一起参与掘金新人创作活动,开启写作之路。
背景
- 目前项目基于 React + antd 开发,现有的换肤方案对于一个大型项目来说,改动成本太大
目标
- 项目中接入成本低
- 技术实现复杂度低
- 满足动态换肤需求
注意事项
使用本方案需要注意以下几点
- 该功能通过动态修改
CSS Variable
实现,因而在 IE 中页面将无法正常展示。 - 该功能在
antd@4.17.0-alpha.0
版本起支持。 - 该功能要求一次性导入全部样式(792kb),会导致一定的性能损耗。(下文会提到优化方法,文件大小减小至 50kb,性能提高 10 倍)
如何使用
引入 antd.variable.css
替换当前项目引入样式文件为 CSS Variable
版本:
-- import 'antd/dist/antd.min.css';
++ import 'antd/dist/antd.variable.min.css';
注:如果你使用了 babel-plugin-import
,需要将其去除。
静态方法配置
调用 ConfigProvider
配置方法设置主题色:
import { ConfigProvider } from 'antd';
ConfigProvider.config({
theme: {
primaryColor: '#25b864',
},
});
优化庞大的 css 文件
前面提到需要全局引入 antd.variable.css 文件,将会导致以下几个问题:
- 该文件 773kb,会有一定的网络消耗(这可不能忍)
- 会覆盖原有样式,导致部分页面样式错乱(项目太大,影响范围不可控,这更不能忍)
解决方案
从 antd.variable.css
文件中,使用正则提取 css3 variable
相关的样式。这样既可以缩小文件体积,又可以达到不覆盖样式的效果。
代码实现如下:(github地址)
const fs = require("fs");
// 提取css选择器
const cssSelectorReg = /\}[^\}]+\{/g;
// 提取css值
const cssValueRreg = /\{[^\{}]+\}/g;
const data = fs.readFileSync("./antd.variable.css", "utf-8");
const cssSelectorMatch = data.match(cssSelectorReg);
const cssValueMatch = data.match(cssValueRreg);
const htmlCss = 'html ' + cssValueMatch[0];
const otherCss = fs.readFileSync("./other.css", "utf-8");
let result = [];
cssSelectorMatch.forEach((item, index) => {
const cssSeletor = item.replace('}', '');
const cssValue = cssValueMatch[index + 1].replace('{', '');
let cssRule = cssSeletor + cssValue;
// 筛选出包含var的
if (cssValue.includes('var(')) {
if (cssSeletor.includes('@')) {
cssRule += '\n}';
}
result.push(cssRule)
}
})
// 保存 去除非var属性 之后的内容
const cssString = result.join('');
const cssLineArr = cssString.split('\n');
// 筛选出带 var 变量的 css 属性,并且添加上 important 属性
const cssVarLineArr = cssLineArr.filter(line => line.includes('var(') || !line.includes(';'))
const cssVarLineStr = htmlCss + cssVarLineArr.join('\n') + otherCss;
fs.writeFile('./antd.variable.revision.css', cssVarLineStr,'utf8',function(err){
if(err)
console.log('写文件出错了,错误是:'+err);
else
console.log('ok');
})
瘦身前的 css 文件内容
瘦身后的 css 文件内容(只剩 css variable 相关的内容)
OK,完美!
Other
下面分享个踩坑记录
项目中采用动态路由,并且 css 也是按需加载的。这样的话,后面加载进来的 css 样式覆盖了前面的样式,导致 css variable 属性被覆盖(动态换肤颜色不生效)。
此时,解决方案有两种:
1、监听路由变化,之后通过 js 重新动态加载一遍该文件
2、提取 css3 variable
相关的样式的时候,顺便在每一行样式的后面添加 !important
属性。这个实现也简单,只需要把 const cssVarLineArr = cssLineArr.filter(line => line.includes('var(') || !line.includes(';'))
这行代码改成 const cssVarLineArr = cssLineArr.filter(line => line.includes('var(') || !line.includes(';')).map(line => line.replace(');', ') !important;'))
即可
结语
作者:HashTang
个人空间:hxkj.vip
转载请注明出处:juejin.cn/post/703743…
欢迎提问交流!