前端主题切换的几种实现

几种方案实现主题切换,适用于预设主题和自定义主题,可以根据实际需要和实现复杂度灵活选择。

滤镜

使用css filter,直接进行页面整体颜色转换,进行主题切换

1
2
3
4
5
6
7
const switchTheme = theme => {
if (theme === 'dark') {
document.body.style.filter = 'invert(100%)';
} else {
document.body.style.filter = 'invert(0%)';
}
};

直接整体使用的话,不仅会将元素进行过滤,同时对图片、视频也会生效,需要特殊处理,让图片、视频保持正常显示。

1
2
3
img:not([src*='.svg']), video {
filter: invert(100%);
}

一些特殊节日需要使网站整体变灰

1
2
3
:root {
filter: grayscale(100%);
}

link标签动态引入(主题变量)

提前定义几套CSS主题样式文件,切换主题时,创建link标签动态插入到head标签中,或者动态修改link标签的href属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export function preLoadStyles(styles: Array<string>, callback?: () => void) {
let rest = styles.length;
for (const path of styles) {
const style = document.createElement('link');
style.setAttribute('rel', 'stylesheet');
style.setAttribute('type', 'text/css');
style.setAttribute('href', path);
/*style.onload = () => {
if (--rest === 0) {
callback?.();
}
document.head.removeChild(style);
style.onload = null;
};*/
document.head.appendChild(style);
}
}

优点:

  1. 实现按需加载,提升首屏加载性能
  2. 修改CSS变量即可修改主题,新增/修改方便

缺点

  1. 动态加载样式文件,如果文件过大可能有加载延迟,切换不流畅

加载全部主题文件(主题变量),用类名进行切换

将所有主题文件引入,切换整体的类名,使不同主题生效

1
2
3
function change(theme) {
document.body.className = theme;
}

优点:

  1. 切换样式不会卡顿

缺点

  1. 首屏加载时间增加

CSS变量+动态setProperty

前面方法主要适用于预设好的几种主题,此方案适用于用于根据颜色面板自定义主体色

定义一个工具类方法,用于修改指定的CSS变量值,调用的是CSSStyleDeclaration.setProperty

1
2
3
4
5
// 预设主体色
:root {
--theme-color: #333;
--theme-background: #eee;
}
1
2
3
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
dom.style.setProperty(prop, val)
}
1
setCssVar('--theme-color', color)

优点:

  1. 不用重新加载样式文件,切换流畅
  2. 可以实时自定义主体色

缺点

  1. 暂无

总结

固定预设主题样式:适合link标签动态引入(主题变量)
主题样式不固定:适合CSS变量+动态setProperty