前端主题切换方案简易版


日常逛网站,尤其是一些大厂官网可以看到有暗黑白天模式的主题切换功能,在一些中后台项目中也会涉及到这种功能的需求,在学习了之后我会总结一些主题切换方案在本期的文章中,有兴趣的小伙伴一起来学习一下吧!

一、link标签动态引入

这个的做法就是准备几套CSS主题样式文件,在需要的时候,创建link标签动态的加载到head标签中,或是动态改变link标签的href属性

效果如下:

网络请求效果如下:

优点:

  • 实现了按需加载,提高了首屏加载时的性能

缺点:

  • 动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟导致样式卡顿的情况
  • 如果主题样式表内定义不当,发生优先级问题,导致样式不是预想效果
  • 各个主题样式是固定写死,后续针对某一主题样式表修改或者新增主题会很麻烦

二、提前引入主题样式做类名间切换

这种方案与第一种比较类似,为了解决反复加载样式文件问题提前将样式全部引入,在需要切换主题的时候将指定的根元素名更换,相当于直接做了样式覆盖,在该类名下的各个样式就统一地更换了,基本实现如下:

/* day样式主题 */
body.day .box {
  color: #f90;
  background: #fff;
}
/* dark样式主题 */
body.dark .box {
  color: #eee;
  background: #333;
}

.box {
  width: 100px;
  height: 100px;
  border: 1px solid #000;
}
<div class="box">
  <p>hello</p>
</div>
<p>
  选择样式:
  <button onclick="change('day')">day</button>
  <button onclick="change('dark')">dark</button>
</p>
function change(theme) {
  document.body.className = theme;
}

效果如下:

优点:

  • 不用重新加载样式文件,所以切换时不会有卡顿现象

缺点:

  • 首屏加载时会牺牲一些加载样式资源
  • 如果主题样式表内定义不当,也会有优先级问题
  • 各个主题样式写死,后续不方便个性化

三、CSS变量 + 类名切换

参考:Vue3官网

Vue3官网有一个暗黑模式切换按钮,点击之后就会平滑地过渡,虽然Vue3中也有一个v-bind特性可以实现动态样式绑定,但经过观察以后Vue官网并没有采取这个方案,针对Vue3v-bind特性在接下来的方案中会细说。
大体思路跟方案2相似,依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量对应的取值即可。
顺带提一下,在Vue3官网还使用了color-scheme: dark;将系统的滚动条设置为了黑色模式,使样式更加统一。

html.dark {
  color-scheme: dark;
}

方案如下:

/* 定义根作用域下的变量 */
:root {
  --theme-color: #333;
  --theme-background: #eee;
}
/* 更改dark类名下变量的取值 */
.dark{
  --theme-color: #eee;
  --theme-background: #333;
}
/* 更改pink类名下变量的取值 */
.pink{
  --theme-color: #fff;
  --theme-background: pink;
}

.box {
  transition: all .2s;
  width: 100px;
  height: 100px;
  border: 1px solid #000;
  /* 使用变量 */
  color: var(--theme-color);
  background: var(--theme-background);
}

效果如下:

优点:

  • 不用重新加载样式文件,切换时也不会卡顿
  • 在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题
  • 新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换

缺点:

  • 兼容性(目前大多不考虑)
  • 首屏加载会牺牲一些时间加载样式资源

四、SCSS + mixin + 类名切换

主要运用SCSS的混合+CSS类名切换,其原理主要是将使用到mixin混合的地方编辑为固定的CSS以后,再通过类名切换去做样式的覆盖,实现如下:

定义SCSS变量:

/* 字体定义规范 */
$font_samll:12Px;
$font_medium_s:14Px;
$font_medium:16Px;
$font_large:18Px;

/* 背景颜色规范(主要) */
$background-color-theme: #d43c33;//背景主题颜色默认(网易红)
$background-color-theme1: #42b983;//背景主题颜色1(QQ绿)
$background-color-theme2: #333;//背景主题颜色2(夜间模式)

/* 背景颜色规范(次要) */ 
$background-color-sub-theme: #f5f5f5;//背景主题颜色默认(网易红)
$background-color-sub-theme1: #f5f5f5;//背景主题颜色1(QQ绿)
$background-color-sub-theme2: #444;//背景主题颜色2(夜间模式)

/* 字体颜色规范(默认) */
$font-color-theme : #666;//字体主题颜色默认(网易)
$font-color-theme1 : #666;//字体主题颜色1(QQ)
$font-color-theme2 : #ddd;//字体主题颜色2(夜间模式)

/* 字体颜色规范(激活) */
$font-active-color-theme : #d43c33;//字体主题颜色默认(网易红)
$font-active-color-theme1 : #42b983;//字体主题颜色1(QQ绿)
$font-active-color-theme2 : #ffcc33;//字体主题颜色2(夜间模式)

/* 边框颜色 */
$border-color-theme : #d43c33;//边框主题颜色默认(网易)
$border-color-theme1 : #42b983;//边框主题颜色1(QQ)
$border-color-theme2 : #ffcc33;//边框主题颜色2(夜间模式)

/* 字体图标颜色 */
$icon-color-theme : #ffffff;//边框主题颜色默认(网易)
$icon-color-theme1 : #ffffff;//边框主题颜色1(QQ)
$icon-color-theme2 : #ffcc2f;//边框主题颜色2(夜间模式)
$icon-theme : #d43c33;//边框主题颜色默认(网易)
$icon-theme1 : #42b983;//边框主题颜色1(QQ)
$icon-theme2 : #ffcc2f;//边框主题颜色2(夜间模式)

定义混合mixin:

@import "./variable.scss";

@mixin bg_color(){
  background: $background-color-theme;
  [data-theme=theme1] & {
    background: $background-color-theme1;
  }
  [data-theme=theme2] & {
    background: $background-color-theme2;
  }
}
@mixin bg_sub_color(){
  background: $background-color-sub-theme;
  [data-theme=theme1] & {
    background: $background-color-sub-theme1;
  }
  [data-theme=theme2] & {
    background: $background-color-sub-theme2;
  }
}

@mixin font_color(){
  color: $font-color-theme;
  [data-theme=theme1] & {
    color: $font-color-theme1;
  }
  [data-theme=theme2] & {
    color: $font-color-theme2;
  }
}
@mixin font_active_color(){
  color: $font-active-color-theme;
  [data-theme=theme1] & {
    color: $font-active-color-theme1;
  }
  [data-theme=theme2] & {
    color: $font-active-color-theme2;
  }
}

@mixin icon_color(){
    color: $icon-color-theme;
    [data-theme=theme1] & {
        color: $icon-color-theme1;
    }
    [data-theme=theme2] & {
        color: $icon-color-theme2;
    }
}

@mixin border_color(){
  border-color: $border-color-theme;
  [data-theme=theme1] & {
    border-color: $border-color-theme1;
  }
  [data-theme=theme2] & {
    border-color: $border-color-theme2;
  }
}
<template>
  <div class="header" @click="changeTheme">
    <div class="header-left">
      <slot name="left">左边</slot>
    </div>
    <slot name="center" class="">中间</slot>
    <div class="header-right">
      <slot name="right">右边</slot>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Header',
    methods: {
      changeTheme () {
        document.documentElement.setAttribute('data-theme', 'theme1')
      }
    }
  }
</script>

<style scoped lang="scss">
@import "../assets/css/variable";
@import "../assets/css/mixin";
.header{
  width: 100%;
  height: 100px;
  font-size: $font_medium;
  @include bg_color();
}
</style>

效果如下:

使用mixin混合在SCSS编译后同样也是将所有包含的样式全部加载

这种方案最后得到的结果与方案2类似,只是在定义主题时由于是直接操作SCSS变量,会更加灵活

优点:

  • 不需要重新加载样式文件,在样式切换时不会有卡顿
  • 在需要切换主题的地方利用mixin混合绑定变量即可,不存在优先级问题
  • 新增或修改主题方便灵活,仅需新增或修改SCSS变量即可,经过编译后将所有主题全部编译出来

缺点:

  • 首屏加载会牺牲一些时间加载资源

总结

link 标签动态引入文件过大切换延时不太推荐;方案二提前引入所有主题做类名切换可以推荐;方案三CSS变量+类名切换是利大于弊可以推荐;方案四:SCSS+mixin+类名切换极力推荐,最终呈现与二类似、定义和使用更加灵活

回见!


文章作者: feico
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 feico !
评论
  目录