前端主题色切换的终极解决方案
Hello大家好,我是日拱一卒的不浪,坚持从工作和生活中不断学习,提炼,沉淀,最终建立自己的强势领域!这是2024年输出的第6/100篇文章,欢迎志同道合的朋友一起学习交流;
公众号:攻城师不浪 绿泡泡:brown_7778
需求背景
最近在工作当中遇到一个需求,做一个后台管理系统,但是我们只做内容区,头部导航
以及左侧菜单栏
都不需要我们做,最后页面需要嵌套进兄弟部门的系统中,最终我们选择的用iframe
的方案进行嵌套。
这时候需求就很明确了,因为切换主题的按钮在头部导航栏中,所以说我们是没法直接控制这个按钮的,只能通过iframe的src
进行传参,然后我们根据这个参数去判断该用哪个主题色。
需求分析
看了网上比较火的几个方案介绍
这里边介绍的大多是利用css的变量去解决,对我有一定的启发,但是我的需求里还需要在js中切换主题色,就比如echarts
图表里的tooltip
,也是要根据主题色进行变换的。
tooltip: {
textStyle: {
color: #fff // 如果是黑色主题,就需要浅灰色了
},
backgroundColor: #fff,
}
用过echarts
的都知道这个配置是在js里进行的,那我要怎么才能在js里统一获取到主题色的变量呢?
js中使用css变量
我猜,一定还有部分同学不知道如何在js中使用css变量,其实我也是最近才学会的技巧。
创建主题文件
这里我们结合我的项目使用less举例,css也是同样的道理。首先新建src/style/light.module.less
,注意了,文件类型是module.less
// 亮色主题
@color-primary: #0a8cfa;
:export {
--color-primary: @color-primary;
}
全局引用
定义了less变量,我想要在项目的任何地方都能直接使用,以vite
编译工具为例,在vite.config.js
里配置
export default defineConfig({
//...
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true, // 允许在Less中使用js语法
additionalData: `
@import "${path.resolve(__dirname, 'src/style/light.module.less')}";
`
}
}
}
//...
})
配置之后,就可以在项目的任何一个页面使用less变量
// less代码
.box {
color: @color-primary;
}
js中使用
还是结合项目,我使用的vue3
,所以在script
标签中使用
<script setup>
import cssVar from '@/style/light.module.less'
const primaryColor = cssVar['--color-primary'] // 输出#0a8cfa
</script>
感兴趣的同学可以尝试打印一下cssVar
,它是一个对象,会把light.module.less
里的:export
内容以一个对象的形式全部输出。
主题色切换
创建暗色主题
我们已经创建了一个light.module.less了,接下来再创建一个暗色系:dark.module.less
// 暗色主题
@color-primary: #000;
:export {
--color-primary: @color-primary;
}
保持变量名一致,只是值
不同。
主题色文件按需加载
通过路由拿到主题参数参数dark,例如
xxx?theme=dark
然后在router.beforeEach全局前置守卫钩子
处进行判断,定义route文件,例如/src/route/route.js
import { createRouter, createWebHashHistory } from 'vue-router'
import { createPinia } from 'pinia'
import { useGlobalStore } from '@/store/global'
const routes = [
//...
]
// 创建路由
export const router = createRouter({
// hash 模式。
history: createWebHashHistory(),
routes
})
export const pinia = createPinia()
// 设置html标签的style属性,将所有的主题色变量都添加到行内样式
const setStyle = (styles) => {
if (styles) {
for (let key in styles) {
document.getElementsByTagName('html')[0].style.setProperty(`${key}`, styles[key])
}
}
}
// 全局前置守卫,进每个页面前都会触发该钩子
router.beforeEach(async (to, from, next) => {
const { theme } = to.query
const globalStore = useGlobalStore()
// 根据路由参数判断需要加载的主题色文件
await import(`@/style/${theme}.module.less`).then((module) => {
// module.default就是主题色文件导出的所有颜色变量
if (module.default) {
// pinia创建的全局状态
globalStore.setTheme(module.default, theme)
setStyle(module.default)
}
next()
})
})
export default routes
setStyle做的事情就是将所需的变量都塞进html的style标签里,如下:
塞进之后,我们就能够在css里直接使用主题色变量了
.box {
background: var(--color-primary)
}
也可以直接在html标签中使用:
<div style="color: var(--color-primary)">测试模板使用</div>
再看下useGlobalStore
做的事情,当路由参数发生变化时,及时切换全局状态,创建/src/store/global.js
import { defineStore } from 'pinia'
export const useGlobalStore = defineStore('global', {
state: () => {
return {
theme: {}, // 主题色配置
themeName: 'light' // 主题色名称
}
},
actions: {
// 当路由参数发生变化时,及时切换全局状态
setTheme(theme, themeName) {
this.theme = theme
this.themeName = themeName
}
}
})
js获取切换后的主题色变量
在js中,通过全局store获取更新后的主题色变量
<script setup>
import { useGlobalStore } from '@/store/global'
const globalStore = useGlobalStore()
// 更新后的主题色,获取的是一个对象
const theme = globalStore.theme
</script>
echarts的主题色不更新
如果项目中用到echarts,并且tooltip也会随着主题色变换而更新,此时你会发现颜色并没有随着全局store的theme的变化而更新,如下:
const chartOption = ref({})
const drawRateChart = () => {
chartOption.value = {
title: [
//...
],
tooltip: {
trigger: 'item',
textStyle: {
color: globalStore.theme['--color-primary']
},
backgroundColor: globalStore.theme['--color-primary']
},
//...
}
}
目前我的做法是监听
全局store的themeName,当它发生改变时,重新执行一遍chartOption的赋值
watch(
() => globalStore.themeName,
(newVal, oldVal) => {
if (newVal !== oldVal) {
drawRateChart()
}
}
)
如果有更好的方案,欢迎评论区交流。
总结
- 利用module.less可以导出变量的能力;
- 利用路由全局前置守卫
beforeEach
按需加载主题色文件; - 根据路由参数更新html标签的行内主题色变量;
- 利用状态管理在js中获取更新后的主题色变量;
- echarts如何更换主题色;
好了,以上就是根据最近的奇葩
业务需求,我所给出的方案,如果有更加优雅的方案,欢迎和我(brown_7778)探讨~
如果在成长的路上感到孤单,可以交个朋友:brown_7778,围观我的朋友圈,每天输出技术、科技、认知与感悟,让我们携手并进。
如果觉得文章对你有帮助,欢迎点赞``关注``转发
,你的鼓励是支持我持续原创下去的动力~
转载自:https://juejin.cn/post/7341797865108766746