# 一步一步实现element-theme主题切换
曾经玩QQ空间,就有切换主题功能,一直想给自己的系统也加上切换主题功能,今天终于抽出时间迈出一步,查阅element主题相关文档,先根据文档进行简单操作,但是过程十分艰辛,一度把项目跑死掉(捂脸)。接下来讲讲踩坑经历。
# 踩坑
根据文档,模仿如上代码,代码顺利运行,且改变primary变量,确实会改变主题的颜色。于是此处算是顺利度过,接下来继续查阅文档
查阅到此处,以为上面生成的 element-variable.scss 文件里面的变量 就是 给上面用的,于是将此处生成的变量名都粘贴到上面的文件,结果项目都跑不起来了,报各种问题。后面继续查阅文档,才发现,是两种方式。完全不一样的。
原来上述生成的文件是用来编译的,编译完再引入,然后编译默认不会实时编译,如需实时编译 加上参数 -w 且编译后的目录默认是 根目录下 ./theme 文件夹 如果想自定义 可 加上 参数 -o ./src/theme
然后引入方式 改为
import "@/theme/index.css"
此处说明下 安装和跑项目会遇到的坑
WARNING
npm i element-theme -g node版本12以下
npm i element-theme-chalk -D node版本 12以上
et -i node版本 12以下
运行项目,node12以上
et -w node 12以下 然后改变主题变量值就会实时刷新
# 具体操作
切换node版本到12 以下,点击前往学习nvm 管理员方式打开终端
# 全局安装 element-theme npm i element-theme -g
切换node版本到12 以上,点击前往学习nvm 管理员方式打开终端
# 在项目根目录安装 element-theme-chalk npm i element-theme-chalk -D
切换node版本到12 以下,点击前往学习nvm 管理员方式打开终端
# 执行以下命令 会生成 element-variables.scss 文件 et -i
修改element-variables.scss文件变量值
# 执行下列命令会生成主题css文件 et
项目main.js文件引入主题文件
import "../themes/index.css"
此时文件变量就能用
但是改变变量值,不能实时刷新,且生成文件的目录不是理想的目录结构,因此可以执行下列操作
et -w -o ./src/theme
-w 可以实时实时编译,-o 可以自定义生成文件目录位置
然后重新引入对应路径的主题文件
至此我们可以使用我们自定义的主题。但是还不能实现切换不同主题
TIP
et命令是根据element-variables.scss 文件生成主题变量文件
每当想生成不同主题的变量值时,就得改到这个文件,为了避免混淆,我们得保存主题对应的变量文件
normal-element-variables.scss
night-element-variables.scss
这样需要编辑的时候,就可以把这个文件内容覆盖element-variables.scss,确定完再把element-variables.scss文件覆盖对应主题变量文件。
# 实现切换主题功能
实现主题切换有两种方式,一种就是使用命名空间的方法,将样式文件以命名空间的形式存在,然后切换类名。
一种就是动态切换引入的主题样式文件,但是样式文件还是得使用命名空间的命名方式,因为相同类名,可能会导致样式不刷新
# 命名空间方式
- 使用et命令生成多套主题样式文件
- 使用gulp生成对应命名空间的主题变量
- 引入全部主题变量,切换类名即可实现主题切换
命名空间方式,故名思议,就是让每个样式前面添加一个主题命名空间
.title {
color: pink;
}
//命名空间后就变成
.theme.title {
color: pink;
}
那么多的样式,难道要一个一个给样式新增命名空间? 作为程序员,当然不能用那么笨的方法。我们可以借助构建工具 gulp,帮我们实现命名空间。
# 搭建gulp环境
//1.安装gulp
npm install gulp
//2.安装gulp-clean-css
npm install gulp-clean-css
//3.安装gulp-css-wrap
npm install gulp-css-wrap
# 根目录新建一个gulpfile.js文件
//引入path
var path = require("path")
//引入gulp
var gulp = require("gulp")
//引入gulp-clean-css
var cleanCSS = require("gulp-clean-css")
//引入gulp-css-wrap
var cssWrap = require("gulp-css-wrap")
//创建一个命令(任务) normal-theme 执行此命令,会进行如下操作
gulp.task("normal-theme", function() {
//导入文件
return gulp.src(path.resolve("./src/themes/normal/index.css"))
/* 找需要添加命名空间的css文件,支持正则表达式 */
.pipe(cssWrap({
selector: ".bu-normal" /* 添加的命名空间 */
}))
.pipe(cleanCSS())
.pipe(gulp.dest("./src/styles/themes/normal")) /* 存放的目录 */
})
//把字体文件也导入
gulp.task("normal-theme-font", function() {
return gulp.src(["./src/themes/normal/fonts/**"]).pipe(gulp.dest("./src/styles/themes/normal/fonts"))
})
//创建一个命令(任务) normal-theme 执行此命令,会进行如下操作
gulp.task("night-theme", function() {
//导入文件
return gulp.src(path.resolve("./src/themes/night/index.css"))
/* 找需要添加命名空间的css文件,支持正则表达式 */
.pipe(cssWrap({
selector: ".bu-night" /* 添加的命名空间 */
}))
.pipe(cleanCSS())
.pipe(gulp.dest("./src/styles/themes/night")) /* 存放的目录 */
})
//把字体文件也导入
gulp.task("night-theme-font", function() {
return gulp.src(["./src/themes/night/fonts/**"]).pipe(gulp.dest("./src/styles/themes/night/fonts"))
})
执行上述命令,就会生成两套命名空间的样式。
# 使用主题
computed: {
//获取当前主题
theme() {
return this.$store.state.setting.theme
}
},
watch: {
theme: {
handler(val) {
//监听主题,然后设置主题命名空间 的类名
const body = document.getElementsByTagName("body")
body[0].className = val
},
immediate: true
}
},
这样就可以切换element-ui的主题了。但是对于普通我们自定义的变量,还不能跟随主题变换,这个最后面一起说。
# 动态添加主题css样式文件
对于需要很多套主题的情况下,上面的方式就会把这两个主题都打包在了项目里,如果你的项目主题需要七八种,这种模式就不适合了。我们就需要动态的加载css。
# 生成多套主题文件
生成多套主题文件,并执行命令生成相应命名空间方式的文件,然后放到服务器供链接访问。
# 使用
computed: {
theme() {
return this.$store.state.setting.theme
}
},
watch: {
theme: {
handler(val) {
const body = document.getElementsByTagName("body")
body[0].className = val
const theme = val
this.setHeadStyle(theme)
},
immediate: true
}
},
methods: {
/**
* @description: 动态加载主题样式
* @return {*}
* @author: syx
*/
setHeadStyle(themeName) {
var head = document.getElementsByTagName("HEAD").item(0)
var style = document.createElement("link")
//给样式添加一个id 用于切换主题时删除标签
style.id = "theme_link"
style.href = `服务器访问路径${themeName}/index.css`
style.rel = "stylesheet"
style.type = "text/css"
const themeLinkTag = document.getElementById("theme_link")
if (themeLinkTag) {
head.removeChild(themeLinkTag)
}
head.appendChild(style)
}
}
这样就可以实现多套element-ui主题的动态切换了。
# 最全样式切换
上面说的切换主题仅仅只是切换了 element-ui的主题。对于我们自定义的变量,并不会切换,所以我们要将自己的主题变量也按不同主题来控制。
因此我们需要用到强大的scss。
目录结构大致如下
src
styles
themes //主题文件夹
index.scss //引入所有主题
normal.scss //正常模式主题变量
night.scss //夜间模式主题变量
themes //其实把这主题放在服务器上,项目以链接方式访问更ok
night //基于element-variables.scss 生成的主题样式文件
night-gulp //经过gulp命令生成的命门空间样式文件
normal //基于element-variables.scss 生成的,然后使用gulp构建的命名空间样式
normal-gulp //经过gulp命令生成的命门空间样式文件
// /src/styles/themes/index.scss
@import "./night.scss";
@import "./normal.scss";
$themes: (
normal: $normal_theme,
night: $night_theme
);
//遍历主题map
@mixin themeify {
@each $theme-name,
$theme-map in $themes {
//global 把局部变量强升为全局变量
$theme-map: $theme-map !global;
//判断html的data-theme的属性值 #{}是sass的插值表达式
//& sass嵌套里的父容器标识 @content是混合器插槽,像vue的slot
[data-theme="#{$theme-name}"] & {
@content;
}
}
}
//声明一个根据Key获取颜色的function
@function themed($key) {
@return map-get($theme-map, $key);
}
//获取背景颜色
@mixin background_color($color) {
@include themeify {
background-color: themed($color) !important;
}
}
//获取字体颜色
@mixin font_color($color) {
@include themeify {
color: themed($color) !important;
}
}
//获取边框颜色
@mixin border_color($color) {
@include themeify {
border-color: themed($color) !important;
}
}
//获取边框颜色
@mixin border_line_color($color,$num:1px) {
@include themeify {
border: $num solid themed($color) !important;
}
}
// 阴影
@mixin box-shadowr($shadows,$color) {
@include themeify {
box-shadow: $shadows themed($color) !important;
}
}
//还可以自定义其他属性....
// /src/styles/themes/normal.scss
$night_theme: (
main_color: #217acb,
}
// /src/styles/themes/night.scss
$night_theme: (
main_color: #001e3d,
}
然后加载项目时先加载主题变量
//vue.config.js
module.exports = {
css: {
// 是否使用css分离插件 ExtractTextPlugin
//如果需要css热更新就设置为false,打包时候要改为true
extract: false,
// 开启 CSS source maps?
sourceMap: true,
// css预设器配置项
loaderOptions: {
sass: {
// @/ is an alias to src/
// so this assumes you have a file named `src/variables.scss`
prependData: `@import "@/styles/themes/index.scss";`
}
}
},
}
页面上使用主题变量
//设置背景颜色
@include background_color("main_color");
//设置字体颜色
@include font_color("main_color");
//设置边框颜色
@include border_color("main_color");
//设置 1px边框 1px可不传 或传 其他像素值
@include border_line_color("main_color", "1px");
//设置 阴影
@include shadowr("main_color");
在store状态管理器新增一个theme主题变量值
<template>
<div id="app">
<!-- 页面切换容器 -->
<router-view />
</div>
</template>
<script>
export default {
name: "App",
computed: {
theme() {
return this.$store.state.setting.theme
}
},
watch: {
theme: {
handler(val) {
//命名空间切换样式类
const body = document.getElementsByTagName("body")
body[0].className = val
const theme = val
this.setHeadStyle(theme)
window.document.documentElement.setAttribute("data-theme", theme)
},
immediate: true
}
},
methods: {
/**
* @description: 动态加载主题样式
* @return {*}
* @author: syx
*/
setHeadStyle(themeName) {
var head = document.getElementsByTagName("HEAD").item(0)
var style = document.createElement("link")
style.id = "theme_link"
style.href = require(`@/themes/${themeName}-gulp/index.css`)
style.rel = "stylesheet"
style.type = "text/css"
const themeLinkTag = document.getElementById("theme_link")
if (themeLinkTag) {
head.removeChild(themeLinkTag)
}
head.appendChild(style)
}
}
}
</script>
<style>
#app {
}
</style>
//引入path
var path = require("path")
//引入gulp
var gulp = require("gulp")
//引入gulp-clean-css
var cleanCSS = require("gulp-clean-css")
//引入gulp-css-wrap
var cssWrap = require("gulp-css-wrap")
//创建一个命令(任务) normal-theme 执行此命令,会进行如下操作
gulp.task("normal-theme", function() {
//导入文件
return gulp.src(path.resolve("./src/themes/normal/index.css"))
/* 找需要添加命名空间的css文件,支持正则表达式 */
.pipe(cssWrap({
selector: ".normal" /* 添加的命名空间 */
}))
.pipe(cleanCSS())
.pipe(gulp.dest("./src/themes/normal-gulp")) /* 存放的目录 */
})
//把字体文件也导入
gulp.task("normal-theme-font", function() {
return gulp.src(["./src/themes/normal/fonts/**"]).pipe(gulp.dest("./src/themes/normal-gulp/fonts"))
})
//创建一个命令(任务) normal-theme 执行此命令,会进行如下操作
gulp.task("night-theme", function() {
//导入文件
return gulp.src(path.resolve("./src/themes/night/index.css"))
/* 找需要添加命名空间的css文件,支持正则表达式 */
.pipe(cssWrap({
selector: ".night" /* 添加的命名空间 */
}))
.pipe(cleanCSS())
.pipe(gulp.dest("./src/themes/night-gulp")) /* 存放的目录 */
})
//把字体文件也导入
gulp.task("night-theme-font", function() {
return gulp.src(["./src/themes/night/fonts/**"]).pipe(gulp.dest("./src/themes/night-gulp/fonts"))
})
WARNING
执行gulp命令时,别忘了同时字体文件也要执行一下。
gulp normal-theme
gulp normal-theme-font
gulp 还得在 node12版本以下运行
TIP
上述目录结构不是您喜欢的也可修改代码,生成别的目录结构
# 总结
实践是检验真理的唯一标准,坚持就是胜利。珍爱头发,原理编程。