大家下午好。今天我们来介绍一种比较简单易实现的主题切换方案,接下来我将尽可能的把这种方案讲的清清楚楚明明白白。写的不好的地方也请大家伙见谅。
正文之前
近来,在项目上突遇主题切换的需求,一般来说是系统预设几套主题,如 浅|亮色(light)、暗色(dark)等等,其中还涉及到不同主题图片等的静态资源变更,以及echarts等的图表颜色变更。接下来本文将介绍较为简单的实现方案。
一: 主题切换方案
目前较为主流的主题切换方案主要有两种:
1. 方案一: css 变量切换主题(推荐)
- 优点:
- 简单,兼容性好,适合大部分项目,兼容绝大多数现代主流浏览器;
- 新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换;
- 在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题;
- 不用重新加载样式文件,在样式切换时不会有卡顿
- 缺点:
- 不兼容ie(但可忽略不计);
- 首屏加载时会牺牲一些时间加载样式资源
2. 方案二: 使用sass/scss、less等预处理器切换主题(推荐,本文暂不介绍)
- 优点:
- 不用重新加载样式文件,在样式切换时不会有卡顿;
- 在需要切换主题的地方利用mixin混合绑定变量即可,不存在优先级问题;
- 新增或修改主题方便灵活,仅需新增或修改SCSS变量即可,经过编译后会将所有主题全部编译出来
- 缺点: 首屏加载时会牺牲一些时间加载样式资源
3. 方案三: 使用传统方案切换主题,如 动态加载link,类名切换等(不推荐,本文不会介绍)
- 优点:
- 兼容性最佳;
- 实现了按需加载,提高了首屏加载时的性能
- 缺点:
- 代码量多,需提前写好几套样式代码,比较繁琐;
- 主题样式表内定义不当,会有优先级问题;
- 动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅;
- 各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦
实现效果
css变量实现

CSS变量实现过程
实现思路
- 定义预设主题并导入
- 定义全局主题变量
theme - 在
html根元素加入data-theme自定义属性 - 使用
var(--bg-color)取相对应的色值,如果需要js动态取值变更,则使用getPropertyValueAPI 取值赋值即可 - 动态绑定也可直接使用
var(--bg-color)即可 - 静态资源通过
require动态加载
1. 创建theme.css,定义预设变量

这里咱只定义了两套主题,浅色与暗系,大家有多套主题也是一样的。然后在 main.js 导入主题。
2. 创建 store,定义全局变量 theme

定义全局变量且保存在本地缓存中(localStorage/cookie/sessionStorage 都可以),全局变量可以使用 vuex, pinia 等,由于本文实现是使用 vue2.6.11,所以在本文中采用 vuex 去定义操作。
3. 在 html 跟元素添加自定义属性

-
在
HelloWorld组件中定义一个切换主题按钮,点击切换组件时触发vuex变更主题的mutations,随即更改主题并且给根元素定义data-theme属性并赋值 -
根据
data-theme属性的值辨别当前主题,结合咱第一步创建的预设主题变量,再使用var()取主题变量就能完成初步主题切换的需求了 -
以
app.vue为例,使用var()实现切换
效果

可以看到,咱背景颜色,字体颜色已然完美实现了主题切换的需求。
4. 静态资源变更, js 取值赋值等
在上面已经完成了组件在 css 中切换主题色,但是切换的时候有时候需要把静态资源也同步更新,以图片为例:切换主题时,把 vue 的 logo 切换为 react 的 logo。
-
在
assets文件夹下根据主题值新建文件夹,然后就可以根据theme值的变化使用require去变更静态资源
-
动态绑定

js 取主题值变更,一般应用在 echarts 图表类或者其他不能直接使用 var() 取切换的场景。
-
创建
echarts柱状图
-
为了方便取值,本人在
utils中定义了getPropertyValue方法,getPropertyValueAPI 作用是返回元素的css值,在前面使用getComputedStyle为了拿到计算后最终的css属性,与之对应的还有setProperty('background', 'red')属性,作用是给元素设置新的css属性值。
-
效果如下

-
这样就可以使用
getPropertyValue配合echarts实现切换主题了,还记得前面定义预设主题吗,我们定义了--charts-bar-bg--charts-bar-color两个变量。监听theme的变化,从而改变echarts的色值
完美

问题
按照以上步骤能实现主题切换,但是此时一刷新就会发现,页面会回归到默认的浅色主题,因为在页面刚加载时是没有 data-theme 属性的,因此我们得在页面加载的时候把 data-theme 加上。
