前端主题切换方案
提前加载所有主题样式,切换时修改类名
实现
思路:提前将所有样式文件引入,然后在切换主题是给一个公共的元素添加一个类名,通过样式的层级来覆盖原有颜色相关样式
main.js中提前引入样式文件:
import '@/assets/css/dark.css'
import '@/assets/css/green.css'
green.css绿色主题css文件:
body.green .content {
color: #333;
background-color: #c7edcc;
}
原来的样式:
.content {
color: #333;
background-color: #fff;
}
切换主题时给公共的元素添加相对应类型来覆盖原有颜色样式:
<button @click="changeStyle('dark')">切换成黑色主题</button>
<button @click="changeStyle('green')">切换成绿色主题</button>
changeStyle(color) {
document.body.className = color
}
优缺点
1》优点
不用重新加载样式文件,在样式切换时不会有卡顿
2》缺点:
首次就加载了所有的主题样式文件,牺牲了一点首屏时间来加载样式
一定要注意优先级的问题,主题切换的样式优先级要高于原有的才能覆盖
不是很灵活,没添加一个主题就要添加一个主题样式文件
link标签动态引入
实现
思路:也和前面一样先写好几套主题样式,然后切换时修改link标签的href属性
document.getElementById('#theme').href = 'green.css'
优缺点
1》优点
实现了按需加载,首屏更好
2》缺点
动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅
一定要注意优先级的问题,主题切换的样式优先级要高于原有的才能覆盖
不是很灵活,没添加一个主题就要添加一个主题样式文件
CSS变量+类名切换
和第一种方案类似,只不过不用写那么多样式,相当于方案1的优化版
CSS定义变量
--两个短横线用于定义CSS变量
<div class="origin">
祖先元素
<div class="father">
父亲
<div class="child">
儿子
<div class="grandson">儿子的儿子</div>
</div>
</div>
</div>
.origin {
background-color: var(--theme-bg);
color: var(--theme-font);
}
.father {
--theme-bg: yellowgreen;
--theme-font: red;
}
.child {
background-color: var(--theme-bg);
color: var(--theme-font);
}
.grandson {
background-color: var(--theme-bg);
color: var(--theme-font);
}
父元素(祖先元素)定义了css变量,子元素(后代元素)就可以通过var()来使用了
var()
语法:
var(<custom-property-name> ,<declaration-value>?)
var()是一个css函数,可以插入一个自定义属性,用来代替非自定义属性中 值的任何部分
第一个参数
作用:要替换的自定义属性的名称
第二个可选参数
可选参数
作用:用作回退值。如果第一个参数引用的自定义属性无效,则该函数将使用第二个值
:root
是一个伪类,用于匹配文档的根元素
1》可以直接在里面设置样式,就相当于给html设置样式
:root {
background-color: #ccc;
padding: 0 20px;
}
2》声明全局css变量,要以 "--" 开头,因为:root是根元素,根元素中定义变量就是全局变量啦
:root {
--pink-bg-color: pink;
--green-color: green;
}
通过var()函数就可以使用全局css变量了
.box {
background-color: var(--main-bg-color);
color: var(--green-color);
}
color-scheme
配色方案,normal就是浏览器默认配色方案,light白天模式,dark夜间模式,写了两个就是有个按顺序的优先级
color-scheme: normal;
color-scheme: light;
color-scheme: dark;
color-scheme: light dark;
color-scheme
的作用范围很有限,包括表单控件、滚动条和 CSS 系统颜色(浏览器内置的颜色)的使用值
可以搭配css变量等进行主题切换
实现
思路:依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量对应的取值即可
具体实现:
引入主题样式文件:theme.css
:root {
--bg-color: #eee;
--font-color: #333;
}
.red {
--bg-color: #a61840;
--font-color: #fbddc2;
}
.green {
--bg-color: #efa5a9;
--font-color: #ede1bc;
}
.blue {
--bg-color: #4e3bb1;
--font-color: #dfb9c8;
}
通过var()给盒子设置颜色
.colorful-wrapper {
background-color: var(--bg-color);
color: var(--font-color);
border-color: var(--border-color);
}
html.red {
color-scheme: red;
}
html.green {
color-scheme: green;
}
html.blue {
color-scheme: blue;
}
切换主题时:给根元素添加对应的类名
document.documentElement.className = color
优缺点
优点:
不用重新加载样式文件,在样式切换时不会有卡顿
在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题
新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换
缺点:
首屏加载时会牺牲一些时间加载样式资源,除了IE的兼容优点问题基本上可以不用考虑兼容问题
CSS变量+动态setProperty
setProperty
语法:
style.setProperty(propertyName, value, priority)
propertyName:属性名
value:属性值,可选参数;如果没有指定,则当作空字符串;不能直接在value后加 !import, 设置important需要使用priority来设置
priority:优先级,可选参数,允许设置"important" 优先级
返回值为undefined
实现
通过:root定义全局css变量,给对应盒子设置颜色时通过var()函数将css变量值设置给对应属性
<style>
:root {
--bg-color: #f5f5f5;
--font-color: #333;
--border-color: #ccc;
}
.colorful-box {
background-color: var(--bg-color, #f5f5f5);
color: var(--font-color, #333);
border-color: var(--border-color, #ccc);
}
</style>
修改主题:通过setProperty来修改变量的值
export const changeTheme = function (theme, ele = document.documentElement) {
for (let [key, value] of Object.entries(themes[theme])) {
ele.style.setProperty(key, value)
}
}
export const themes = {
red: {
'--bg-color': '#a61840',
'--font-color': '#fbddc2',
},
green: {
'--bg-color': '#efa5a9',
'--font-color': '#ede1bc',
},
blue: {
'--bg-color': '#4e3bb1',
'--font-color': '#dfb9c8',
},
}
优缺点
1》优点:
不用重新加载样式文件,在样式切换时不会有卡顿
新增或修改主题方便灵活
2》缺点:
IE的兼容其他基本浏览器基本不用太考虑
SASS的mixin混入+类名切换
theme.scss
// 白色主题
$bg-color-light: #fff;
$font-color-light: #333;
$border-color-light: #999;
$bg-image-light: url("src/images/light.png");
// 黑色主题
$bg-color-dark: #999;
$font-color-dark: #fff;
$border-color-dark: #000;
$bg-image-dark: url(src/images/dark.png");
mixin.scss
@import "./theme.scss";
@mixin bg_image($img) {
background-image: $img;
[data-theme="dark"] & {
background-image: $bg-image-dark;
}
[data-theme="light"] & {
background-image: $bg-image-light;
}
}
@mixin font_color($color) {
color: $color;
[data-theme="dark"] & {
color: $font-color-dark;
}
[data-theme="light"] & {
color: $font-color-light;
}
}
@mixin border_color($color) {
border-color: $color;
[data-theme="dark"] & {
border-color: $border-color-dark;
}
[data-theme="light"] & {
border-color: $border-color-light;
}
}
@mixin bg_color($color) {
background-color: $color;
[data-theme="dark"] & {
background-color: $bg-color-dark;
}
[data-theme="light"] & {
background-color: $bg-color-light;
}
}
vue文件中使用
<template>
<div class="wrapper">
<div>
<button @click="setTheme('light')">白天模式</button>
<button @click="setTheme('dark')">深夜模式</button>
</div>
<div class="content">文字text</div>
<div class="img"></div>
</div>
</template>
<script>
export default {
methods: {
setTheme(theme) {
document.documentElement.setAttribute("data-theme", theme);
},
},
};
</script>
<style lang="scss" scoped>
@import "../assets/scss/mixin.scss";
.wrapper {
@include bg_color($bg-color-dark);
@include font_color($bg-color-dark);
}
.img {
width: 800px;
height: 400px;
border: 1px solid red;
@include bg_image($bg-image-dark);
}
</style>
优缺点:
优点:
灵活
兼容性好
缺点:
vue3才支持
在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能)
Vue3
基本使用
v-bind可以绑定data中的变量
原理其实就是给元素绑定CSS变量,在绑定的数据更新时调用 CSSStyleDeclaration.setProperty 更新CSS变量值
const theme = {
color: "red",
};
.box {
color: v-bind("theme.color");
}
优缺点:
优点:
灵活,易用,切花不需要重新加载样式文件
缺点:
vue3才支持
在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能)
如果主题样式不确定,用户可自定义,使用 CSS变量+动态setProperty 最合适
如果主题固定使用选择更广,大部分都是选择 CSS变量+类名切换 这种方案