前言
本文介绍前端主题切换的常见解决方案。
分两个部分:
-
- 需要考虑浏览器兼容性的场景(例如H5页面的开发);
-
- 无需考虑浏览器兼容性的场景。
这两个部分差异较大,针对场景1,笔者采用的是名为css-vars-ponyfill
的第三方库(补丁),针对场景2,笔者则是完全依靠css完成主题切换的功能。
需要考虑浏览器兼容性的场景
1. 安装css-vars-ponyfill
shell
yarn add css-vars-ponyfill
2. 获取当前主题token
对于h5页面的开发,一般来说都是从客户端的cookie中获取当前主题字段的,如下所示:
js
const theme = getCookie("themeMode");
上面的getCookie函数作用为:从cookie中获取对应key的value, 详见[getCookie](解读JavaScript中cookie操作函数getCookie的实现原理和应用场景 - 掘金 (juejin.cn))
3. 根据当前的主题返回css键值对
构造一个名为getThemeVars
的函数,其入参就是刚从cookie中获取的主题,返回值为类似于下的键值对:
js
{
...,
"--hc-primary-color": "#2b3c4d",
}
4. 构造initTheme函数,初始化css变量
js
import cssVars from "css-vars-ponyfill";
...
export function initTheme() {
cssVars({
rootElement: document,
variables: getThemeVars(),
shadowDOM: false,
include: 'link[rel=stylesheet],style',
onFinally: function(hasChanged, hasNativeSupport, benchmark) {
console.log('onFinally', hasChanged, hasNativeSupport, benchmark)
}
})
}
这段代码的作用是使用 "css-vars-ponyfill" 库来初始化主题。
首先 ,通过 import cssVars from "css-vars-ponyfill"
的语句导入了 "css-vars-ponyfill" 库。
然后 ,定义了一个名为 initTheme
的函数,用于初始化主题。在函数内部,调用了 cssVars()
方法,并传入一些配置选项对象。
配置选项的含义如下:
rootElement: document
:设置根元素为整个文档(document
对象)。variables: getThemeVars()
:设置 CSS 变量的值,getThemeVars()
是一个函数,用于获取 CSS 变量的值。shadowDOM: false
:指定是否处理 Shadow DOM 附件中的样式,默认为 false。include: 'link[rel=stylesheet],style'
:设置引入 CSS 的方式,通过选择器指定要包含的<link rel="stylesheet">
和<style>
元素。onFinally: function(hasChanged, hasNativeSupport, benchmark) {...}
:在最终处理完成后触发的回调函数,其中hasChanged
表示 CSS 变量是否有变化,hasNativeSupport
表示浏览器是否原生支持 CSS 变量,benchmark
表示处理所花费的时间。
最后 ,在 onFinally
的回调函数中使用 console.log()
打印出 hasChanged
、hasNativeSupport
和 benchmark
的值。
通过这段代码,可以实现在旧版浏览器中使用 CSS 变量的功能,并根据需要进行自定义配置。该代码将应用指定的 CSS 变量值,并在完成处理后进行回调。
5. 初始化主题颜色
以React项目创建的H5页面被客户端调用为例:
在整个项目的入口文件App.tsx中,直接调用initTheme
方法即可。这一块的逻辑可以表示如下图:
无需考虑浏览器兼容性的场景(参考渡一教育)
如果使用的浏览器支持最新的CSS变量语法,那么只需要通过修改适配在html元素上的css变量的值就可以了。
1. 在全局样式文件中构建不同主题下css变量的值
项目的主题设置在全局样式文件中,一般是index.css
中
css
:root {
--text-color: #fff;
--bg1: #102128;
--bg2: #2d5567;
}
html[data-theme='dark'] {
--text-color: #333;
--bg1: #c7ffdd;
--bg2: #fbd988;
}
.btn {
color: var(--text-color);
background-color: var(--bg1);
}
这里必须 保证html[data-theme='dark']
的权重大于:root
.
2. 在项目的入口js文件中引入全局css文件
js
import 'src/index.css';
3. 提供主题的全局控制函数
一般来说,全局函数会放在项目的src/utils/index.js
文件中:
js
let theme = 'light';
export const getTheme = () => theme;
export const setTheme = (_theme) => void theme = _theme;
这样通过调用getTheme或者setTheme函数就可以获取、设置theme的值了。
4. getTheme的使用场景
假设这样的场景,如果主题是夜晚就在页面的右上角显示"夜晚, 漆黑的夜晚..."这个文案;而主题如果是白天就显示"白昼, 热烈的阳光..."
html
import { getTheme } from 'src/utils/index.js';
<span>{getTheme()==='light' ? '白昼, 热烈的阳光...' : '夜晚, 漆黑的夜晚...'}</span>
5. 将theme和html的属性绑定起来
只需要在设置主题色的时候将html中名为data-theme的值修改即可,例如:
js
let theme = 'light';
export const getTheme = () => theme;
export const setTheme = (_theme) => {
theme = _theme;
document.documentElement.dataset.theme = _theme;
};
6. 解决刷新之后主题不保存
实现如果用户在使用过程中刷新了页面,则上述代码无法保证刷新之后恢复到刷新前的主题中去。因此,需要本地储存介入,分成两步:
-
- 在项目的入口文件中尝试读取localStorage中主题相关字段的值,并调用setTheme设置到全局变量theme中去。
-
- 每次设置完新的theme之后都将最新的值同步到localStorage中去。
一般来说,localStorage中的key会保存到src/utils/constant.js
文件中:
js
export const THEME_KEY = 'theme_mode';
然后在项目的入口文件中:
js
import {setTheme} form 'src/utils/index.js';
import {THEME_KEY} form 'src/utils/constant.js';
try {
const localTheme = localStorage.getItem(THEME_KEY);
if(localTheme) setTheme(localTheme);
} catch (e) {
console.log('init theme mode fail!');
}
总结
本文介绍了两种前端项目的主题切换策略,如果对您有所帮助,麻烦点个赞呗~