大型复杂项目中使用 antd 最新特性实现运行时低成本动态换肤

2,553 阅读3分钟

这是我的第一篇博客,一起参与掘金新人创作活动,开启写作之路。

背景

  • 目前项目基于 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 文件内容

image.png

瘦身后的 css 文件内容(只剩 css variable 相关的内容)

image.png

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…

欢迎提问交流!