如果是新项目,可以选择使用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替换,算不上高级,但是有用。欢迎广大网友提建议。
版权归原作者 SHEN_JIAMEI 所有, 如有侵权,请联系我们删除。