几种实现主题切换的方式
1. 利用 prefers-color-scheme 特性
prefers-color-scheme
是CSS
媒体特性【@media】用于检测用户是否有将操作系统的主题色设置为亮色【light】或者暗色【dark】。
当前prefers-color-scheme
新特性支持各大主流电脑(window
和IOS
系统,Linux
系统可以用第三方工具)端浏览器谷歌、火狐等,包括手机端的安卓和苹果,足以说明prefers-color-scheme
属性已经稳定成熟,可以用于生产环境了。
prefers-color-scheme
的使用也很简单,直接需要在全局css
文件中添加以下代码
:root {
color-scheme: light dark;
}
js
可以通过window.matchMedia()
来监听系统主题。
const scheme = window.matchMedia("(prefers-color-scheme: dark)");
if (scheme.matches) {
console.log("深色模式");
} else {
console.log("浅色模式");
}
除了自动跟随系统外,也可以手动切换。不过需要结合css
变量,详情请看第四点。
优点:
- 简单
缺点:
- 不能自定义深浅主题样式
2. 切换 link
通过改变link
的href
。
优点
- 按需加载
缺点
- 动态加载样式,样式文件过大或者网络较慢的情况下会导致样式切换不流畅
- 存在样式优先级问题
- 后续新增样式或者修改比较麻烦
3. 引入所有主题样式,通过类名切换
类似第二种,不过为了解决反复加载样式文件问题采用提前将样式全部引入的方式,在需要切换主题的时候将指定的根元素类名更换,相当于直接做了样式覆盖,在该类名下的各个样式就统一地更换了
.light-scheme {
background: #fff;
}
.dark-scheme {
background: #1b1b1b;
}
function change(scheme) {
const content = document.getElementsByTagName("body")[0];
content.setAttribute("class", scheme);
}
优点:
- 不用重新加载样式文件,切换时不会卡顿
缺点:
- 文件较大会导致首屏加载慢
- 存在优先级问题
- 后续新增样式或者修改比较麻烦
4. css 变量 + 类名切换
大体思路跟方案 2 相似,依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好 CSS
变量,只需要在不同的主题下更改 CSS
变量对应的取值即可。
:root {
--color-background: #1b1b1b;
--color-text-default: #0b1016;
--white-color-background: #fff;
}
.light-scheme {
background: var(--white-color-background);
color: var(--color-text-default);
}
.dark-scheme {
background: var(--color-background);
color: white;
}
let flag = true;
const content = document.getElementsByTagName("body")[0];
const but = document.getElementsByTagName("button")[0];
but.onclick = function () {
flag = !flag;
if (flag) {
content.setAttribute("class", "light-scheme");
} else {
content.setAttribute("class", "dark-scheme");
}
};
优点:
- 不用重新加载样式文件,在样式切换时不会有卡顿
- 在需要切换主题的地方利用
var()
绑定变量即可,不存在优先级问题 - 新增或修改主题方便灵活,仅需新增或修改
CSS
变量即可,在var()
绑定样式变量的地方就会自动更换
缺点:
- 首屏加载时会牺牲一些时间加载样式资源
前面提到css
变量还可以结合prefers-color-scheme
一起使用实现跟随系统的自定义深浅主题样式。
:root {
--color-background: #1b1b1b;
--color-text-default: #0b1016;
--white-color-background: #fff;
}
/* 监听操作系统主题模式 */
@media (prefers-color-scheme: dark) {
body {
background-color: var(--color-background);
}
}
@media (prefers-color-scheme: light) {
body {
background-color: var(--white-color-background);
}
}
5. css 变量 + 动态 setProperty
适合自定义颜色背景
只需在全局中设置好预设的全局 CSS
变量样式,无需单独为每一个主题类名下重新设定 CSS
变量值,因为主题是由用户动态决定。
定义一个工具类方法,用于修改指定的 CSS
变量值,调用的是 CSSStyleDeclaration.setProperty
export const setCssVar = (
prop: string,
val: any,
dom = document.documentElement
) => {
dom.style.setProperty(prop, val);
};
在样式发生改变时调用此方法即可
setCssVar("--theme-color", color);
优点:
- 不用重新加载样式文件,在样式切换时不会有卡顿
- 仔细琢磨可以发现其原理跟方案 5 利用
Vue3
的新特性v-bind
是一致的,只不过此方案只在:root
上动态更改CSS
变量而Vue3
中会将CSS
变量绑定到任何依赖该变量的节点上。 - 需要切换主题的地方只用在
:root
上动态更改CSS
变量值即可,不存在优先级问题 - 新增或修改主题方便灵活
缺点:
- 首屏加载时会牺牲一些时间加载样式资源(相对于前几种预设好的主题,这种方式的样式定义在首屏加载基本可以忽略不计)
6. vue3 专属的 v-bind
<script setup>
// 这里可以是原始对象值,也可以是ref()或reactive()包裹的值,根据具体需求而定
const theme = {
color: "red",
};
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind("theme.color");
}
</style>
Vue3
中在 style
样式通过 v-bind()
绑定变量的原理其实就是给元素绑定 CSS 变量,在绑定的数据更新时调用 CSSStyleDeclaration.setProperty
更新 CSS
变量值。