umi + antd 动态主题色

简介: 这篇文章讲解的是动态主题色的变化,也就是,页面可能会有10种,或者20种颜色需要切换,不知道到底有多少种颜色;同时,文档也考虑到多人协助开发,开发人员只需要按照约定方式去编写样式、主题文件名、目录等命名规范即可。

效果如下:

1.gif


1 前言


本人项目使用的 ui 库为 antd,样式为 less。


主题色的变化,antd 官网提供了相关的方案:定制主题,但是,该方案是静态的换肤,也就是已经知道系统需要什么样的主题色,根据相关的配置,antd 自动做转化。


这篇文章讲解的是动态主题色的变化,也就是,页面可能会有10种,或者20种颜色需要切换,不知道到底有多少种颜色;同时,文档也考虑到多人协助开发,开发人员只需要按照约定方式去编写样式、主题文件名、目录等命名规范即可。


主要思路:动态插入样式,覆盖系统已经编译好的相关样式,包括 UI 组件库 和 自定义样式。


2 实现


步骤一:在 Umi 里配置主题


如果你在使用 Umi,那么可以很方便地在项目根目录的 .umirc.tsconfig/config.ts 文件中 theme 字段进行主题配置。theme 可以配置为一个对象或文件路径。

"theme": {
  "primary-color": "#1DA57A",
},

或者 一个 js 文件

"theme": "./theme.js",

本人使用的是公司自己的框架,基于 Umi 进行二次封装,所以,在 本目录下的config/theme.ts 配置 @primary-color,就实现了默认主题色的配置。


步骤二:新建相关目录和文件


在根目录下,新建public目录,引入less.4x.min.js,具体在项目中 GitHub 代码库中下载;同时新建 styles目录,创建 antd.theme.lesscomponents.lesscustom.theme.less 三个文件;



  • custom.theme.less:放置扫描项目所有自定义主题的样式;


  • components.less:引入 antd.theme.less 和 custom.theme.less,如下:
@import "./antd.theme.less";
@import "./custom.theme.less";


说明:为啥是 public 目录呢?因为 Umi 项目,public 目录下所有文件会被 copy 到输出路径,也就是相关的资源,是被直接放到项目根目录。


具体如下图:

2.png


步骤三:编写 scripts/theme/themeScripts.js 脚本


新建 scripts/theme/themeScripts.js 文件,编写如下脚本,该脚本的主要作用是扫描项目中所有的 xxx.theme.less文件,写入到 custom.theme.less 文件中。

// 主题色脚本
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const { promisify } = require('util');
const themeVars = require('./../../config/theme');
const glob = promisify(require('glob'));
const root = './';
const customPath = './public/styles/custom.theme.less';
Main();
async function Main() {
  init();
  start();
}
function init() {
  // 清空
  fs.writeFileSync(customPath, '', 'utf8');
  // 主题色变量初始值,跟 src/utils/index.ts 中 less.modifyVars 定义的变量相对应。
  for(let key in themeVars) {
    fs.appendFileSync(customPath, `${key}: ${themeVars[key]};\n`, 'utf8');
  }
}
async function start() {
  const rootPath = path.resolve(root);
  const fileList = await glob('src/**/*.theme.less', { cwd: rootPath, ignore: ['node_modules/**', 'src/.umi/**', 'src/.umi-production/**'] });
  // global.theme.less 在最前面,优先级最低
  const otherList = [];
  const globalFile = fileList.filter(file => {
    if (file.indexOf('global.theme.less') > -1) {
      return true;
    }
    otherList.push(file);
    return false;
  });
  globalFile.concat(otherList).forEach((filePath) => {
    writeFile(rootPath, filePath);
  });
  console.info(chalk.greenBright('恭喜您, 主题文件扫描完成!!!')); // eslint-disable-line
}
// 写文件
function writeFile(rootPath, filePath) {
  const bufferContent = fs.readFileSync(path.resolve(rootPath, filePath));
  const content = bufferContent.toString('utf8');
  fs.appendFileSync(customPath, `\n// ${filePath}\n${content}`, 'utf8');
}
module.exports = Main;


注意:node 使用的是 CommonJS 服务器端 js 模块化的规范,所以对模块化导入导出需要使用 module.exports,主题色变量直接使用了项目初始化配置的 config/theme.ts 变量,所以,需要把文件从 ts 改为 js,同时 config/theme.ts 的导出需要改为 module.exports 导出,如下图:


                                                                     .png


步骤四:编写 utils 主题色方法


下面的方法主要是使用 less.js,动态插入自定义的相关样式,利用 less.modifyVars,传入相关动态主题色参数,改变自定义的相关样式。


根据如下代码,自定义的样式文件为 /styles/components.less,该文件是作为统一的入口,引入 antd 和 自定义样式。


自定义的变量有@header-bar-visible 和 @primary-color;

function _changeTheme(themeColor: string) {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  if (!(window as any).less) return;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (window as any).less.modifyVars({
    '@primary-color': themeColor,
    '@header-bar-visible': 'visible',
  });
}
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 目标下
    // https://lesscss.org/usage/#api
    // env: 'production' development
    lessConfigNode.innerHTML = `
        window.less = {
          env: 'production',
          async: true,
          javascriptEnabled: true
        };
        `;
    lessScriptNode.src = '/less.4x.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);
  }
}


3  package.json 引入执行主题色脚本


在 package.json 的 scripts 引入执行主题色脚本,同时,在 start 和 build 之前,每次自动执行扫描主题色,如下:

"scripts": {
  "start": "npm run theme && umi dev",
  "build": "npm run theme && umi build",
  "theme": "node scripts/theme/themeScripts.js",
},


同时,安装脚本需要的库,glob 和 chalk:

npm i glob chalk -S


4 页面组件样式编写


经过以上步骤,就已经大功告成了,开发人员只需要编写 xxx.theme.less 即可,不再需要编写其他配置文件。


约定,相关的主题颜色需要抽成 xxx.theme.less,如编写 List 组件,List.less 和 List.theme.less,如下图:


less 样式需要使用 prefix,不能使用 import styles from './Less.less' 这种写法,因为如果使用该写法,则样式编译会被加上 hash,动态主题色覆盖就无法覆盖了。antd 官网组件也是使用 prefix 样式写法。


4.png


注意:自定义文件 xxx.theme.less 使用 css 写法,而不是采用 @prefix 前缀变量写法,因为 @prefix 写法有时候会失效,正常样式写法示例如下:

.c-list-more{
  color: @primary-color;
}
.c-list-more:hover{
  color: @primary-color;
}
.c-list-item .title::before{
  background-color: @primary-color;
}
.c-list-item .title:hover{
  color: lighten(@primary-color, 15%);
}
.c-list-item .desc{
  color:  @primary-color;
}


5 资源


该文章是本人之前写过的两篇文章汇总,用兴趣的同学,可以查看原来的文章,了解更多的思路。


相关文章
|
7月前
|
前端开发 JavaScript
react 修改 antdesign 的 组件默认样式
react 修改 antdesign 的 组件默认样式
278 0
|
4月前
|
JavaScript 前端开发 Shell
实现在vue中自定义主题色彩切换
实现在vue中自定义主题色彩切换
65 0
|
5月前
|
前端开发 JavaScript
vue 一键换肤(切换主题样式)
vue 一键换肤(切换主题样式)
326 0
|
前端开发 JavaScript
Ant-design-vue定制主题色
Ant-design-vue定制主题色
|
7月前
|
前端开发 JavaScript 安全
【亮剑】探讨了在React TypeScript应用中如何通过道具(props)传递CSS样式,以实现模块化、主题化和动态样式
【4月更文挑战第30天】本文探讨了在React TypeScript应用中如何通过道具(props)传递CSS样式,以实现模块化、主题化和动态样式。文章分为三部分:首先解释了样式传递的必要性,包括模块化、主题化和动态样式以及TypeScript集成。接着介绍了内联样式的基本用法和最佳实践,展示了一个使用内联样式自定义按钮颜色的例子。最后,讨论了使用CSS模块和TypeScript接口处理复杂样式的方案,强调了它们在组织和重用样式方面的优势。结合TypeScript,确保了样式的正确性和可维护性,为开发者提供了灵活的样式管理策略。
80 0
|
7月前
|
JavaScript 前端开发
【vue2中引入阿里第三方图标库使用自定义的Ant Design Vue组件a-cascader级联选择后缀图标】
【vue2中引入阿里第三方图标库使用自定义的Ant Design Vue组件a-cascader级联选择后缀图标】
577 0
|
7月前
|
前端开发
自定义elementUI皮肤、色系、主题、主色调
自定义elementUI皮肤、色系、主题、主色调
|
7月前
|
JavaScript
Vuetify 设置主题
Vuetify 设置主题
116 0
|
前端开发
五分钟时间告诉你React全局配置项目主题色是怎么实现的
博主最近在自研Concis组件库,全局配置项目主题色的大体意思其实和antd一样,可以通过一款色系来全局配置Concis中所有组件的主题色,比如Concis默认的色系是蓝色(#1890FF),如果公司业务的主题色是绿色、黄色,一个个去给单独组件加样式是很麻烦的,因此就有了需求的来源
464 1