使用的模板:
https://github.com/jzfai/vue3-admin-ts
迁移项目到新项目的时候,需要改系统的主题颜色,这个模板里可以选择主题颜色,组件大小,和系统语言,这里就取消能修改主题颜色的功能,默认的是base-theme,于是查看了文档,找到主题是这部分的内容,前言 | vue3-admin-plus
发现如果自定义一套主题,其实原理并不难,下面写我看过这个功能后,自己理解的实现该功能的详细的过程。
假如我们有主题色三个,每一个主题肯定都有一个ID,比如:base-theme/lighting-theme/dark-theme
如果实现切换,那么肯定有一个默认的值,假设默认的主题是base-theme。这种默认的配置一般会和设置系统语言,设置组件大小,设置系统名称等静态配置放到一个settings里。
src\settings.js?
import packageJson from '../package.json'
export const settings = {
title: packageJson.name,
/*
* i18n setting default language
* en/zh
* */
defaultLanguage: 'zh',
/*
* default theme
* base-theme/lighting-theme/dark-theme
* */
defaultTheme: 'base-theme',
/*
* setting default defaultSize
* large / default /small
* */
defaultSize: 'default',
}
export default settings
?有了默认的主题色,那么修改主题色肯定有一个方法,将这个settings的值覆盖掉,至于怎么覆盖并怎么改变系统的颜色是之后考虑的。
什么时候需要切换主题:一个是Vue初始化时,读取默认的主题,一个是页面配置主题选项,点击时调用这个方法,去切换主题。如果这个方法是setTheme,那么需要一个参数,主题id。
既然有这个方法存在,那么显然一般这种全局的方法,肯定都是写在store里。
src\store\config.ts
import { defineStore } from 'pinia'
import { langTitle } from '@/hooks/use-common'
import settings from '@/settings'
import { toggleHtmlClass } from '@/theme/utils'
import { i18n } from '@/lang'
export const useConfigStore = defineStore('config', {
state: () => {
return {
language: settings.defaultLanguage,
theme: settings.defaultTheme,
size: settings.defaultSize
}
},
persist: {
storage: localStorage,
paths: ['language', 'theme', 'size']
},
actions: {
// 切换主题色————这里
setTheme(data: string) {
this.theme = data
toggleHtmlClass(data)
},
//设置组件大小
setSize(data: string) {
this.size = data
},
// 设置系统语言
setLanguage(lang: string, title) {
const { locale }: any = i18n.global
this.language = lang
locale.value = lang
document.title = langTitle(title) // i18 page title
}
}
})
这里的theme先读的是默认的主题颜色,如果页面需要切换主题,传一个主题色并存起来,用到persist来保持持久化。
?
persist是将 Vuex store 中的一些状态(
language
、theme
、size
)保存到浏览器的localStorage
中,以便在页面刷新后能够保留这些状态的值。
storage: localStorage
:表示使用浏览器的localStorage
作为持久化存储,保存的数据在页面刷新或关闭后仍然存在。
paths: ['language', 'theme', 'size']
:指定了需要持久化的状态路径。在你的 Vuex store 中,这包括了language
、theme
、和size
这三个状态。这意味着只有这些状态的变化会被保存到localStorage
中,其他状态不会被持久化。这个配置的好处在于,当用户刷新页面或关闭再打开页面时,之前存储在
localStorage
中的状态值会被还原到 Vuex store 中,使应用程序能够保持之前的状态。这对于用户体验和记住用户的个性化设置非常有用,因为用户在一次会话中所做的更改不会因为刷新页面而丢失。Vue3 有了这个可以方便地进行持久化存储。
加了这个配置的效果如下:
至于在哪里切换主题颜色,可以在app.vue的onMounted里先执行一下,读取一下默认主题,之后如果页面需要,就同样地在页面加切换主题色的功能,但是都是setTheme来执行。
上面是指怎么切换主题颜色,如何实现真正切换主题是通过toggleHtmlClass,这个涉及css的一些东西,下面详细讲解主题色实现的过程。?
这里以默认的base-theme为例子来讲。
这里有模板的文档对于主题色实现的原理的解释,如:
在CSS3中,:root是伪类,文档的根元素,比如,我们都知道HTML文件里,HTML文件是根元素,如果?:root定义一个全局变量,那么全局都能用,通过 var()
函数引用它们。这种方式使得可以在一个地方定义主题或颜色变量,然后在整个样式表中方便地引用这些变量,实现一致性的主题设计。
需要注意的是,:root
只是一个约定用法,也可以选择其他选择器来定义全局变量,但通常 :root
是最常见的选择,因为它表示文档的根元素,所以我猜测这个模板实现主题色切换的原理就是通过html的类名,然后更改主题色。
HTML 文档中
<html>
标签的类名可以接受一个类名className
作为参数,然后将<html>
标签的类名设置为这个参数值。通过操作
<html>
标签的类名,可以实现一些在整个页面中生效的样式变更或主题切换。例如,如果在 CSS 中定义了与特定类名相关的样式规则,那么通过切换
<html>
标签的类名,可以在整个页面上切换不同的样式或主题。这是一个简单的示例,假设你有两个不同的样式主题,一个是深色主题,一个是浅色主题:
// 切换到深色主题 toggleHtmlClass('dark-theme'); // 切换到浅色主题 toggleHtmlClass('light-theme');
在这个例子中,
toggleHtmlClass
函数的调用会改变<html>
标签的类名,从而触发不同主题的样式规则。这是一种在整个应用程序中切换主题或样式的通用方式。
比如,我选中一个组件,查看它的类,发现这里就有主题的类名存在:
当我切换主题时,我发现这个类名也随之改变了,那么就清楚实现的原理了。核心代码是这一段:
export const toggleHtmlClass = (className) => {
document.querySelectorAll('html')[0].className = className
}
?这段代码主要是切换 HTML 文档中
<html>
标签的类名。
document.querySelectorAll('html')
:使用document.querySelectorAll
方法选择所有<html>
标签。该方法返回一个 NodeList(节点列表),通过[0]
取得第一个匹配的<html>
元素。
.className = className
:将<html>
元素的类名设置为传入函数的参数className
。这意味着这个函数的目的是动态地改变整个页面的样式,通过修改<html>
标签的类名来触发不同的 CSS 样式规则。就如上面所述,这样就可以切换主题了。
具体如何自定义主题的颜色等样式,这里有一个模板的代码可以查看。?
这里以base-theme为例子,主要的颜色都没有改,因为用的是模块里封装好的自定义主体,所以改起来很容易,后面有时间再详细地了解一下实现过程。
具体的代码,可以看模板里的代码。开头有链接和地址。实现原理在开头的文档里,写的很详细就不多阐述了。
总算了解了一些实现系统主题色的方法,虽然很多细节尚未掌握,但是简单的主题颜色已经能快速地掌握修改啦,over~