前言
这段时间参与了一个开源项目 Sub-Store,前端为 PWA(Progressive Web Apps) 实现,新版前端由我从 0 开始搭建,技术栈为 Vite2、Vue3、Pinia,期间有很多问题,但也逐一解决了。这篇是第一篇总结,关于 前端项目自定义主题 的一些解决方案。
由于 颜值即正义,而且 萝卜青菜,各有所爱,所以要实现一个所有用户都喜欢的主题样式是很难的,并且我也是 UI 设计出身,自然而然的就想到了自定义主题。目前此项目实现了 自动主题和固定主题切换、主题分类选择(黑夜/白天)。
通过 CSS 变量实现主题切换
最初搭建项目时的不同主题实现方案是在 body 添加 theme 属性,使用 SCSS 嵌套选择器来指定颜色样式,但是这种方案不够灵活,因为它只能指定一个主题,而且它的样式不能被继承。
显然我太天真。这种方法有很大的问题:
- 由于使用了 scss 预处理器,它实际上会被编译为 css,scss 变量会变成实际的值,并且多个主题都会同时写入运行时 css 文件,这样会如果项目过大或主题过多时 css 文件过大。
- 这种写法当可选主题很多,十个二十个时候,每种主题都需要单独的 scss 文件,这样会导致项目的代码量大,而且项目的维护也会比较麻烦。
- 如果用户更换主题,那么所有的样式都会被重新计算,这样会导致页面的渲染,这是一个比较消耗性能的操作。
由于两套选择器内要写不同的变量名,而且最终变量还是会变成常量,自然而然的就会发问了:有没有什么办法可以让他在运行时也是变量,并且可以动态切换变量值呢?
答案当然是有的 —— css 变量。
使用 CSS 变量
首先,我们需要定义一组 css 变量,然后在不同的 class 内对变量指定对应的的值,这样我们切换主题时只需要切换根节点的 class 即可实现动态切换主题。
自动化
以上的方案还是不够灵活,我每一套主题都要在 css 文件里写一套变量值,且扩展其他功能时也不是很方便。
此项目是 Vue3 技术栈,我使用了自定义 hooks 并配合一个 themes 文件夹来实现更加方便的主题管理。
theme 文件示例:
这里的 meta
字段是主题的信息,name
author
这两个字段是在页面中需要展示的,label
字段主要是用来区分主题类型,以便展示在对应的 select 列表中,extend
则是为了其他人进行自定义主题时更加方便,在遍历时会读取 extend
字段,找到对应的主题文件,然后基于该主题进行扩展。
整体主题切换思路
- hooks 中对
themes
文件夹遍历读取,根据label
字段进行分类放进对应的选择器中,并根据name
author
生成选择器中的显示名称; - 读取当前主题,根据当前主题的
extend
及color
生成当前主题的变量列表,遍历修改 root 中的变量值实现初始化主题; - 当用户切换主题时,根据主题的
extend
及color
新主题的变量列表,遍历修改 root 中的变量值实现切换主题。
这里我根据需要实现了自动和手动切换主题功能,并且添加新主题无需做其他修改,仅需添加一个主题文件,按规范填写字段后,重新打包即可生效。
这套方案目前来说还是比较符合这个项目的需求,一步一步根据项目需求摸索出来的方式,可能不是很通用,仅供参考。