如图官方的修改方法
我的项目中使用了elementui作为框架,同时使用了scss。
有没有办法动态更改这个变量的值来设置不同的主题?
之前的思路是动态引入不同的主题
// 不同的主题文件,他们的内容只有一个就是设置
// $--color-primary: theme1;
// $--color-primary: theme2;
// $--color-primary: theme3;
// 如果使用var变量来会导致构建失败
// $--color-primary: var(--primary, red)
// 通过接口获取到主题
let theme = getTheme()
import(`./${theme}.scss`)
这样存在一个问题,就是实际构建的时候,所有定义的主题都会被编译。就算我没有动态引入也会被编译,导致主题之间互相覆盖。
所以现在想只用一个scss文件来定义变量,但是这个变量的值不是固定的,有没有办法处理呢?
经过不断的尝试和搜索,我发现所有实现方式最终还是和elementUI切换主题源码的实现方式基本一致,即手动替换掉框架原来的颜色。
源码如下:
ementui切换主题源码
如果项目配置的webpack在4.0以上的用户也可以查看这篇文章,对框架的处理原理也是类似的
https://segmentfault.com/a/11...
最终我自己的实现也类似,代码如下,供参考:
import color from 'css-color-function'
// 这里是elementui对应的颜色公式,和elementui源码中的formula一致
import elementFormula from './elementFormula'
// element_ui默认的主题色
const ORIGINAL_THEME = '#409EFF'
const version = require('element-ui/package.json').version // 版本号
// 所有element_ui的默认样式
let themeChalk = null
// RGB颜色转16进制
function convertColor (RGBColor) {
// 转成 xxx, xxx, xxx
let rgbArray = RGBColor.slice(4, RGBColor.length - 1).replace(/s/g, '').split(',')
let red = Number(rgbArray[0]).toString(16)
let green = Number(rgbArray[1]).toString(16)
let blue = Number(rgbArray[2]).toString(16)
return red + green + blue
}
// 获取默认element_ui颜色转换后的数组
function getThemeColorArray (theme) {
let colors = [theme.replace('#', '')]
Object.keys(elementFormula).forEach(key => {
const value = elementFormula[key].replace(/primary/g, theme)
// 根据公式转成对应的rgb颜色,但比对用的是16进制颜色
let RGBColor = color.convert(value)
colors.push(convertColor(RGBColor))
})
return colors
}
/**
* 替换掉所有的旧颜色
* @param {Array} oldStyleArray 老的element_ui颜色数组,使用element_ui默认的主题色生成
* @param {Array} newStyleArray 新颜色数组,通过 getThemeColorArray 方法转换颜色生成
* */
function updateStyle (oldStyleArray, newStyleArray) {
oldStyleArray.forEach((color, index) => {
themeChalk = themeChalk.replace(new RegExp(color, 'ig'), newStyleArray[index])
})
return themeChalk
}
function getCSSString (url, callback) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
themeChalk = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
}
// 转换主题颜色
export function changeThemeColor (primary) {
if (typeof primary !== 'string') return
// 生成新的对应颜色数组
const themeStyleArray = getThemeColorArray(primary)
const getHandler = (id) => {
return () => {
const originalStyleArray = getThemeColorArray(ORIGINAL_THEME)
const newStyle = updateStyle(
originalStyleArray,
themeStyleArray
)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
const themeHandler = getHandler('redcat-theme-style')
// 第一次从cdn获取对应的样式文件
if (!themeChalk) {
const url = `//unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
getCSSString(url, themeHandler)
} else {
themeHandler()
}
}
###关键问题在于编译后的 css 无法动态切换样式,最终还是要通过切换 class 的方式切换主题
看看这篇文章 https://segmentfault.com/a/11...
###以下是我实际工作中利用scss实现主题色动态切换
1、首先需要在vue项目中安装sass相关的依赖
npm install sass-loader --save-dev
npm install node-sass --sava-dev
npm install sass-resources-loader --save-dev
2、在src->assets文件下下创建_theme.scss,里面主要存放所有公共变量、混合样式等
3、全局引入_theme.scss:修改根目录->build文件夹->utils.js里面的scss: generateLoaders('sass'),具体修改内容如下
scss: generateLoaders('sass').concat({
loader: 'sass-resources-loader',
options: {
//
resources: path.resolve(__dirname, '../src/assets/_theme.scss')
}
})
4、demo页面效果如下图:
5、具体功能实现代码如下:
main.vue代码如下:
<template>
<div class="theme-demo">
<!-- 主题色切换按钮 -->
<div>
<el-radio-group v-model="theme" @change="switchTheme">
<el-radio-button label="blue">蓝色主题</el-radio-button>
<el-radio-button label="green">绿色主题</el-radio-button>
<el-radio-button label="orange">橙色主题</el-radio-button>
</el-radio-group>
</div>
<!-- 主题色demo -->
<div class="content">
<el-tabs v-model="activeName">
<el-tab-pane label="用户管理" name="first">用户管理</el-tab-pane>
<el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
export default {
data () {
return {
// 主题颜色
theme: 'blue',
// 当前激活tab
activeName: 'first'
}
},
methods: {
/**
* description: 切换主题
*
*/
switchTheme () {
// 缓存主题颜色
localStorage.setItem('theme', this.theme)
// 切换主题色(前提需要给html标签设置data-theme自定义属性)
window.document.documentElement.setAttribute('data-theme', this.theme)
}
},
created () {
// 获取缓存中的主题颜色
this.theme = this.getItem('theme', this.theme)
// 设置主题色(前提需要给html标签设置data-theme自定义属性)
window.document.documentElement.setAttribute('data-theme', this.theme)
},
}
</script>
<style lang="scss" scoped>
.theme-demo {
.content {
margin-top: 30px;
padding: 30px;
border-radius: 4px;
border: 1px solid;
/* 混入边框颜色样式示例 */
@include border_color();
/deep/ .el-tabs__item:hover {
/* 混入边框颜色样式示例 */
@include text_color();
}
/deep/ .el-tabs__item.is-active {
/* 混入文字颜色样式示例 */
@include text_color();
}
/deep/ .el-tabs__active-bar {
/* 混入背景颜色样式示例 */
@include background_color();
}
/deep/ .el-tabs__content {
/* 使用公共变量 */
font-size: $font-size-base;
min-height: 400px;
text-align: left;
}
}
}
</style>
_theme.scss代码如下:
// ====== 公共变量定义 start ======
// 常用字体大小
$font-size-small: 12px;
$font-size-base: 14px;
$font-size-large: 16px;
// ====== 公共变量定义 end ======
// ====== 混合样式定义 start ======
// 文字颜色
@mixin text_color{
// 蓝色主题
[data-theme="blue"] & {
color: #378EF0;
}
// 绿色主题
[data-theme="green"] & {
color:#37BC64;
}
// 橙色主题
[data-theme="orange"] & {
color:#FF9D14;
}
}
// 边框颜色
@mixin border_color{
// 蓝色主题
[data-theme="blue"] & {
border-color: #378EF0;
}
// 绿色主题
[data-theme="green"] & {
border-color:#37BC64;
}
// 橙色主题
[data-theme="orange"] & {
border-color:#FF9D14;
}
}
// 背景色
@mixin background_color{
// 蓝色主题
[data-theme="blue"] & {
background-color: #378EF0;
}
// 绿色主题
[data-theme="green"] & {
background-color:#37BC64;
}
// 橙色主题
[data-theme="orange"] & {
background-color:#FF9D14;
}
}
// ====== 混合样式定义 end ======
如果对你有帮助,感谢采纳~