在现代前端开发中,主题切换(例如"暗黑模式"与"亮色模式")已逐渐成为提升用户体验的重要功能之一。无论是白天黑夜的自动调整,还是用户手动配置个性化配色,一个灵活、可持久化的主题系统正变得不可或缺。
本文将基于 Vue 3 与 Pinia 状态管理,结合 pinia-plugin-persistedstate 插件,手把手实现一个支持主题切换与自定义主题配置的完整方案。
初始化主题仓库:useThemeStore
我们首先使用 Pinia 创建一个主题管理仓库,用于集中管理当前主题、可选主题列表,以及相关的操作逻辑。这个仓库也会配合 pinia-plugin-persistedstate 实现数据的本地持久化。
✅ 安装依赖(如果尚未安装):
javascript
npm install pinia
npm install pinia-plugin-persistedstate
✅ 在 main.ts 中注册 Pinia 和持久化插件:
javascript
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.mount('#app')
✅ 创建主题仓库:stores/theme.ts
javascript
import { defineStore } from 'pinia'
const useThemeStore = defineStore('theme', {
state: () => ({
theme: 'light',
themes: {
light: {
'--bg-color': '#ffffff',
'--text-color': '#333333',
'--primary-color': '#007bff',
},
dark: {
'--bg-color': '#121212',
'--text-color': '#f0f0f0',
'--primary-color': '#5e8dff',
},
custom: {
'--bg-color': '#f5f5dc',
'--text-color': '#111111',
'--primary-color': '#ff6600',
}
}
}),
getters: {
availableThemes: (state) => Object.keys(state.themes)
},
actions: {
initTheme() {
this.setTheme(this.theme)
},
setTheme(name) {
this.theme = name
const html = document.documentElement
html.className = name
const vars = this.themes[name]
for (const key in vars) {
html.style.setProperty(key, vars[key])
}
},
toggleTheme() {
const keys = Object.keys(this.themes)
const next = keys[(keys.indexOf(this.theme) + 1) % keys.length]
this.setTheme(next)
},
setCustomVar(key, value) {
if (this.theme !== 'custom') {
this.theme = 'custom'
}
this.themes.custom[key] = value
const html = document.documentElement
html.style.setProperty(key, value)
}
},
persist: true,
})
export default useThemeStore
📌 提示:
建议在应用入口调用 themeStore.initTheme() 来确保页面加载时自动应用上次使用的主题。
CSS 变量作用于 document.documentElement,你可以在全局样式中引用这些变量,灵活控制主题样式
如此已经配置好了主题,以下是个使用案例
javascript
<script setup>
import { ref, onMounted } from 'vue'
import useThemeStore from '../store/theme'
const themeStore = useThemeStore()
const bgColor = ref('')
const textColor = ref('')
onMounted(() => {
bgColor.value = themeStore.themes[themeStore.theme]["--bg-color"]
textColor.value = themeStore.themes[themeStore.theme]["--text-color"]
})
const changeTheme = (e) => {
themeStore.setTheme(e.target.value)
}
const changeBgColor = () => {
themeStore.setCustomVar("--bg-color", bgColor.value)
}
const changeTextColor = () => {
themeStore.setCustomVar("--text-color", textColor.value)
}
</script>
<template>
<div class="layout">
<header class="header">
<button @click="themeStore.toggleTheme">下一个主题</button>
<span>头部、当前主题{{ themeStore.theme }}</span>
<select v-model="themeStore.theme" @change="changeTheme"
style="margin-left: 24px;">
<option v-for="item in themeStore.availableThemes" :key="item" :value="item">{{ item }}</option>
</select>
<div v-if="themeStore.theme =='custom'">
背景色:
<input type="color" v-model="bgColor" @input="changeBgColor" />
文字色:
<input type="color" v-model="textColor" @input="changeTextColor" />
</div>
</header>
<aside class="sidebar">
<ul>
<li>菜单一</li>
<li>菜单二</li>
<li>菜单三</li>
</ul>
</aside>
<main class="content">内容区域</main>
</div>
</template>
<style scoped>
.layout {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
background: var(--bg-color);
color: var(--text-color);
padding: 16px;
font-size: 20px;
height: 100px;
}
.sidebar {
background: var(--bg-color);
color: var(--text-color);
width: 180px;
padding: 16px 0;
flex-shrink: 0;
height: 100%;
position: absolute;
top: 100px;
bottom: 0;
}
.layout {
position: relative;
}
.sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar li {
padding: 8px 24px;
cursor: pointer;
}
.content {
background: var(--bg-color);
color: var(--text-color);
margin-left: 180px;
padding: 24px;
flex: 1;
}
</style>
✨ 结语
通过本文,我们基于 Vue 3 + Pinia + pinia-plugin-persistedstate 搭建了一个完整的主题管理系统,支持多主题切换、自定义主题变量以及状态持久化。在实际项目中,这样的设计不仅提升了用户体验,还使主题系统具备了良好的可维护性与扩展性。
无论是构建后台管理系统、博客平台还是移动端应用,一个灵活可控的主题系统,都是提升视觉一致性与用户个性化体验的重要一环。希望这篇文章能为你在项目中实现主题功能提供清晰思路和实用参考。
如果你在实践过程中遇到问题,欢迎留言交流,也欢迎点赞、收藏支持我持续输出优质内容 😊!