一.前言

  之前我的项目须要为其减少主题换色性能,接手这个需要后,就开始剖析我的项目的代码内容。我把他们次要分成两个大类,一是elementUI下的组件,二是自定义模板或者组件。这两类都须要对其款式进行切换来达到换色成果。为了做到尽可能的优雅,最好是只扭转一处中央,也不必反复写一样的款式,就能够实现换色。我这种菜鸡真是花了不少工夫在下面。

二.方案设计

计划1.link标签动静引入或应用不同类名切换不同主题

  设计几套不同主题款式的文件,在须要的时候,创立link标签动静加载到head标签中,或者是动静扭转link标签的href属性。或者在须要切换主题的时候根标签切换对应的类名来切换不同的款式。
  然而这样子的操作的话,如果须要更改某一样式,所有主题都须要更改,麻烦且容易出错,这能够说是最笨的办法。

计划2.应用css变量+类名切换

  这是计划1的改良计划。大体思路跟计划1类似,仍然是提前将款式文件载入,切换时将指定的根元素类名更换。不过这里绝对灵便的是,默认在根作用域下定义好CSS变量,只须要在不同的主题下更改CSS变量对应的取值即可。
  这样子操作的益处就是通过CSS变量,能够达到只改一次就能全副更改的成果。

计划3.应用CSS变量+动静setProperty

  计划3较于前两种会更加灵便,不过视状况而定,这个计划实用于由用户依据色彩面板自行设定各种色彩主题,这种是主题色彩不确定的状况,而前两种计划更实用于定义预设的几种主题。

三.计划实操

  综合思考之后,决定应用第三种计划,因为第三种计划足够灵便,能够通过js设置几个默认主题,也能够交给用户去取色来设置新主题。这样再当前需要有变的时候批改起来更有劣势。

以下内容将以计划3来开展:
在我的项目内新建theme文件夹,搁置如下文件内容:

  如上,index.css与fonts内的字体文件,是预设好的elementUI款式模板,依据用户的取色或者预设的色彩来替换文件内对应色彩,再全局搁置到html的style标签内,即可实现elementUI的主题色彩替换。而自定义模板或者组件的色彩款式则是由另外的文件来实现。

首先先看看elementUI的主题如何替换:

// 装置css-color-function,前面会用来转换色彩是rgb还是16进制npm css-color-function // yarn add css-color-function

通过formula.json获取色彩变动规定,再用generateColors函数转化色彩

// formula.json{  "primary": "color(primary)",  "shade-1": "color(primary shade(10%))",  "shade-2": "color(primary shade(20%))",  "shade-3": "color(primary shade(30%))",  "shade-4": "color(primary shade(40%))",  "shade-5": "color(primary shade(50%))",  "shade-6": "color(primary shade(60%))",  "shade-7": "color(primary shade(70%))",  "shade-8": "color(primary shade(80%))",  "shade-9": "color(primary shade(90%))",  "alpha-1": "color(primary alpha(.1))",  "alpha-2": "color(primary alpha(.2))",  "alpha-3": "color(primary alpha(.3))",  "alpha-4": "color(primary alpha(.4))",  "alpha-5": "color(primary alpha(.5))",  "alpha-6": "color(primary alpha(.6))",  "alpha-7": "color(primary alpha(.7))",  "alpha-8": "color(primary alpha(.8))",  "alpha-9": "color(primary alpha(.9))",  "light-1": "color(primary tint(10%))",  "light-2": "color(primary tint(20%))",  "light-3": "color(primary tint(30%))",  "light-4": "color(primary tint(40%))",  "light-5": "color(primary tint(50%))",  "light-6": "color(primary tint(60%))",  "light-7": "color(primary tint(70%))",  "light-8": "color(primary tint(80%))",  "light-9": "color(primary tint(90%))"}
// color.js  import color from 'css-color-function'import formula from './formula.json' export function generateColors(primary) {  let colors = {}   Object.keys(formula).forEach((key) => {    const value = formula[key].replace(/primary/g, primary)    const c = color.convert(value)    colors[key] = c.indexOf('rgba') > -1 ? c : colorRgbToHex(c)  })   return colors} /* 将rgb色彩转成hex */export function colorRgbToHex(rgb) {  let [r, g, b] = rgb.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')   return '#' + ((1 << 24) + (Number(r) << 16) + (Number(g) << 8) + Number(b)).toString(16).slice(1)}

预设了各个主题的默认色彩供调用。若有多种主题,间接写即可。后续若主题较多,可应用混入性能。

//model.jslet themes = {    default: {        color_block: "#07b6b5",        color_line: "#009aa6"    }}export { themes }

elementUI主题色替换函数代码:

//index.jsimport { generateColors } from './color'let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!./index.css').toString();import formula from './formula.json'let originalStyle = ''export function writeNewStyle(themeColor) {  let colors = generateColors(themeColor)  let cssText = originalStyle  let colorsCssText = ''  Object.keys(colors).forEach((key) => {    cssText = cssText.replace(new RegExp('(:|\\s+)' + key, 'g'), '$1' + colors[key])    colorsCssText += `      .color-${key}{color: ${colors[key]}!important;}      .bg-${key}{background-color: ${colors[key]}!important;}      .border-${key}{border-color: ${colors[key]}!important;}    `  })  console.log("cssText", cssText)  let styleTag = document.getElementById('ide-theme-style')  if (!styleTag) {    styleTag = document.createElement('style')    styleTag.setAttribute('id', 'ide-theme-style')    document.head.appendChild(styleTag)  }  styleTag.innerText = cssText + colorsCssText}export function getIndexStyle(themeColor) {    if (!originalStyle) {        originalStyle = getStyleTemplate(style, themeColor)    }    writeNewStyle(themeColor)  }export function getStyleTemplate(data, themeColor) {  let colors = generateColors(themeColor)  const colorMap = new Map()  Object.keys(formula).forEach((key) => {    colorMap.set(colors[key], key)  })  for (let [key, value] of colorMap) {    data = data.replace(new RegExp(key, 'ig'), value)  }  return data}

自定义模板或者组件替换主题色函数代码:

//theme.jsimport { themes } from "./model";import { getIndexStyle } from "./index.js"const changeStyle = (obj) => {    for (let key in obj) {        document          .getElementsByTagName("body")[0]          .style.setProperty(`--${key}`, obj[key]);    }};// 设置主题的色彩const setThemeColor = (theme) => {    localStorage.setItem("theme", JSON.stringify(theme)); // 保留主题到本地,下次进入应用该主题    changeStyle(theme); // 扭转款式}const setFontFamily = (FontFamilyName) => {    localStorage.setItem("FontFamily", FontFamilyName);    const themeConfig = {        themeFontFamily: FontFamilyName    };    changeStyle(themeConfig);}export const setTheme = (type = 'themeColor') => {    if (type === 'themeColor') {        let currentTheme = localStorage.getItem("theme") && JSON.parse(localStorage.getItem("theme"));        if (!currentTheme) {          currentTheme = themes.default        }        setThemeColor(currentTheme);        getIndexStyle(currentTheme.color_block)    } else if (type === 'FontFamily') {        let FontFamily = localStorage.getItem("FontFamily");        setFontFamily(FontFamily);    }};

以上为theme文件夹文件介绍。调用的入口函数是theme.js文件里的setTheme函数。
须要在我的项目工程的main.js文件内引入setTheme函数。
如:

import { setTheme } from "@/theme/theme";//而后在created生命周期里调用函数// 初始化主题色setTheme && setTheme();

注:须要留神的是,index.css文件的调用能够有两种模式,一种是从后盾获取,一种是从本地调用。后盾获取比拟好了解,然而前端获取CSS文件的纯文本内容就要折腾一下了。过后也花了些工夫去解决。当初给出一个本人的解决方案以供参考:

应用require形式获取文本再转为string:

//css引入let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!./index.css').toString();//scss引入let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!sass-loader!./style.scss').default.toString();//less引入let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!less-loader!./style.less').default.toString();