• Element UI如何切换主题?
  • 发布于 1周前
  • 34 热度
    0 评论
主题切换功能是我们系统中非常常见的功能,大到微软操作系统,QQ软件,小到小型的OA办公软件,基本都能看到切换主题的功能。而我一直想给自己的系统也加上切换主题功能,今天终于抽出时间迈出一步,查阅element主题相关文档,先根据文档进行简单操作,但是过程十分艰辛,一度把项目跑死掉(捂脸)。

实现切换主题功能
 实现主题切换有两种方式,一种就是使用命名空间的方法,将样式文件以命名空间的形式存在,然后切换类名。一种就是动态切换引入的主题样式文件,但是样式文件还是得使用命名空间的命名方式,因为相同类名,可能会导致样式不刷新。

A.命名空间方式
1.使用et命令生成多套主题样式文件
2.使用gulp生成对应命名空间的主题变量
3.引入全部主题变量,切换类名即可实现主题切换
命名空间方式,故名思议,就是让每个样式前面添加一个主题命名空间
.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文件
//堆代码 duidaima.com
//引入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"))
})
执行上述命令,就会生成两套命名空间的样式。

B.使用主题
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"))
})
总结
实践是检验真理的唯一标准,坚持就是胜利。珍爱头发,原理编程。

用户评论