0


2022-03-01 Element UI切换主题、动态换肤功能

如果是新项目,可以选择使用element-plus,或者另寻他路,不要来踩这个坑。

以下方法适用于已经完成的项目,需要加换肤功能。借鉴官方在线切换主题。

demo是新写的,node版本为v14.15.0,sass-loader版本为^12.0.0

https://github.com/jiameiShen/element-theme-demo.git

不想看我写废话的,可以直接去看项目~~~~~


1、引入主题element-variables,打包构建使用,可参考官方文档;

element-ui自定义主题官方文档

npm i element-theme -g  
npm i element-theme-chalk -D
et -i

常见报错

fs.js:45
} = primordials;
    ^

ReferenceError: primordials is not defined
    at fs.js:45:5

解决方案

这里我走了很多弯路,搜到过最多的说法是给node降级,虽然降级到node10x是可用的,但是会对影响项目本身,还会出现其他问题。

最后的解决方案是:

cnpm install element-themex -g

完美解决,然后再执行 : et -i 命令进行编译。

2、全局引入主题配置,方便文件中使用,个人习惯(可省略);

引入主题配置,全局可以使用主题样式,注意,我们的主题颜色是要动态变化的,所以不要在vue文件中使用**$--color-primary以及其衍生的颜色,**其他变量随便用;

// var.scss
/* 参考根目录下的 element-variables */
/* 切记此处不修改配置,以element-variables为准,可适当增加自定义配置 */
/* 如果要修改element-variables,请执行npm run build_theme更新主题 */

@import "element-variables";

// vue.config.js
css: {
    loaderOptions: {
      scss: {
        prependData: () => {
          return `
            @import "@/assets/scss/var.scss";
          `;
        },
      }
    }
  }

常见报错

由于sass-loader版本不同,loaderOptions 中 additionalData的键名也不同,根据自己sass-loader的版本改就好了。

sass-loader v8-,这个选项名是 "data"
sass-loader v8 中,这个选项名是 "prependData"
sass-loader v10+,这个选项名是 "additionalData"

3、重点来了

首先获取已经生成好的css主题文件,然后将主题相关的颜色统一用标志置换,之后每次替换主题色,都去置换主题文件中的标志,即可得到最终的主题文件。

1、把theme相关的js写在一个文件夹下面:

// 安装css-color-function,后面会用到
cnpm i css-color-function -S
// 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)
}
// 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%))"
}
// index.js
import { generateColors } from './color'
import axios from 'axios'
import formula from './formula.json'
import variables from '@/assets/scss/var.scss'

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;}
    `
  })
  document.getElementById('themeStyle').innerText = cssText + colorsCssText
}

export function getIndexStyle() {
  return new Promise((resolve) => {
    if (!originalStyle) {
      axios.get('/theme/index.css').then(file => {
          const fileData = file.data
          originalStyle = getStyleTemplate(fileData)
          resolve()
      })
    } else {
      resolve()
    }
  })
}

export function getStyleTemplate(data) {
  let colors = generateColors(variables.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
}

2、我们要保证生成的新css能覆盖旧css,我用的笨方法,直接在index.html中引入css,同时把main.js里面的主题文件去掉,以免新css无法覆盖;

<link rel="stylesheet" href="<%= BASE_URL %>theme/index.css">
<!-- id为themeStyle后面会用到,不要删,与js文件中的id同步修改 -->
<style id="themeStyle" type="text/css"></style>

3、浅用一下

// main.js中使用
import variables from '@/assets/scss/var.scss'
import { getIndexStyle, writeNewStyle } from '@/tools/theme/index'

/* 初始化主题样式 */
getIndexStyle().then(() => {
  writeNewStyle(variables.themeColor)
})

报错了。。。

这里我们会先得到一个错误,那是因为我们的themeColor是想引用scss里面配置的变量,但是var.scss没有暴露出这个参数,所以我们改一改var.scss。

/* 参考根目录下的 element-variables */
/* 切记此处不修改配置,以element-variables为准,可适当增加自定义配置 */
/* 如果要修改element-variables,请执行npm run build_theme更新主题 */

@import "element-variables";

:export {
  name: "scss"; 
  themeColor: $--color-primary;
}

如果还是报同样的错误,就说明**:export无效**,我们打印variables 会发现得到的是空对象;

常见问题:

我这边直接贴上我的解决方案

// vue.config.js
css: {
    loaderOptions: {
      // 解决scss :export为空的情况
      // https://webpack.js.org/loaders/css-loader/
      css: {
        modules: {
          mode: 'icss',
        },
      },
      sass: {
        // additionalData: `@import "@/assets/scss/var.scss";`,
        // 文件中再引入var.scss会抛错
        // 该写法解决Syntax Error: SassError: This file is already being loaded.
        additionalData: (content, loaderContext) => {
          const { resourcePath } = loaderContext
          if (resourcePath.endsWith('var.scss')) return content
          return `@import "@/assets/scss/var.scss"; ${content}`
        },
      },
    },
  },

4、动态切换颜色

切换颜色随便用什么组件

主要就是使用writeNewStyle

import { writeNewStyle } from '@/tools/theme/index'

const newColor = '#6772E5'

writeNewStyle(newColor)

最后关于怎么保存和使用themeColor我就不赘述了,拜拜~~


总结

demo是新写的,node版本为v14.15.0,sass-loader版本为^12.0.0

https://github.com/jiameiShen/element-theme-demo.git

以上解决方案用的是直接改写源css替换,算不上高级,但是有用。欢迎广大网友提建议。

标签: ui elementui vue.js

本文转载自: https://blog.csdn.net/SHEN_JAIMEI/article/details/122984921
版权归原作者 SHEN_JIAMEI 所有, 如有侵权,请联系我们删除。

“2022-03-01 Element UI切换主题、动态换肤功能”的评论:

还没有评论