vue+vite 全局主题

好的!下面是一个更清晰的项目结构和每个文件的代码内容,我会将每个文件的位置、作用以及内容详细列出,帮助你更好地组织和理解整个项目。


🎨 项目结构

csharp 复制代码
src/
│
├── assets/
│   ├── global.css      # 全局样式,包括主题相关的 CSS 变量
│
├── components/
│   ├── ThemeProvider.vue  # 用于提供主题状态并处理主题切换
│
├── theme-context.ts       # 主题上下文定义
├── App.vue                # 根组件,包含 ThemeProvider
└── main.ts                # 入口文件,初始化应用

1. src/assets/global.css --- 全局样式(包括主题配色)

css 复制代码
/* assets/global.css */
:root {
  --bg-color: #ffffff;      /* 背景色 */
  --text-color: #333333;    /* 文字颜色 */
  --button-bg: #4CAF50;     /* 按钮背景色 */
  --button-text-color: #ffffff; /* 按钮文字颜色 */
  --card-bg: #f4f4f4;       /* 卡片背景色 */
  --link-color: #1E90FF;    /* 链接颜色 */
}

html.dark {
  --bg-color: #121212;      /* 背景色 */
  --text-color: #e0e0e0;    /* 文字颜色 */
  --button-bg: #4CAF50;     /* 按钮背景色 */
  --button-text-color: #ffffff; /* 按钮文字颜色 */
  --card-bg: #2c2c2c;       /* 卡片背景色 */
  --link-color: #1E90FF;    /* 链接颜色 */
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  font-family: 'Arial', sans-serif;
  transition: background-color 0.3s, color 0.3s; /* 为过渡添加动画效果 */
}

a {
  color: var(--link-color);
  text-decoration: none;
}

button {
  background-color: var(--button-bg);
  color: var(--button-text-color);
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

button:hover {
  background-color: darken(var(--button-bg), 10%);
}

.card {
  background-color: var(--card-bg);
  color: var(--text-color);
  padding: 1rem;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

2. src/theme-context.ts --- 主题上下文

typescript 复制代码
// theme-context.ts
import { InjectionKey, Ref } from 'vue'

export type Theme = 'light' | 'dark'

export interface ThemeContext {
  theme: Ref<Theme>
  toggleTheme: () => void
  setTheme: (t: Theme) => void
}

export const ThemeSymbol: InjectionKey<ThemeContext> = Symbol('ThemeContext')

3. src/components/ThemeProvider.vue --- 主题提供器

xml 复制代码
<template>
  <div :class="theme">
    <slot />
  </div>
</template>

<script setup lang="ts">
import { ref, provide, computed, watchEffect } from 'vue'
import { ThemeSymbol, Theme } from '../theme-context'

const theme = ref<Theme>('light')

// 切换主题函数
const toggleTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}

// 设置指定主题
const setTheme = (t: Theme) => {
  theme.value = t
}

// 自动根据本地存储或系统偏好设置主题
if (localStorage.getItem('theme')) {
  theme.value = localStorage.getItem('theme') as Theme
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
  theme.value = 'dark'
}

watchEffect(() => {
  // 将主题保存到 localStorage
  localStorage.setItem('theme', theme.value)
  // 根据主题切换类名
  document.documentElement.className = theme.value
})

provide(ThemeSymbol, {
  theme,
  toggleTheme,
  setTheme,
})

const themeClass = computed(() => theme.value)
</script>

4. src/App.vue --- 根组件,使用 ThemeProvider

xml 复制代码
<template>
  <ThemeProvider>
    <router-view />
  </ThemeProvider>
</template>

<script setup lang="ts">
import ThemeProvider from './components/ThemeProvider.vue'
</script>

5. src/main.ts --- 入口文件,初始化 Vue 应用

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import './assets/global.css'  // 引入全局样式

createApp(App).mount('#app')

6. 使用 inject 获取主题并在组件中使用

xml 复制代码
<template>
  <div class="card">
    <p>当前主题:{{ theme }}</p>
    <button @click="toggleTheme">切换主题</button>
  </div>
</template>

<script setup lang="ts">
import { inject } from 'vue'
import { ThemeSymbol } from '../theme-context'

const themeContext = inject(ThemeSymbol)

if (!themeContext) {
  throw new Error('Theme context not provided')
}

const { theme, toggleTheme } = themeContext
</script>

<style scoped>
.card {
  background-color: var(--card-bg);
  color: var(--text-color);
  padding: 1rem;
}
</style>

📝 小结

  • global.css:定义了全局的主题配色,并通过 CSS 变量控制亮色与暗色主题。
  • theme-context.ts :定义了一个 ThemeContext 类型,用来传递和管理当前的主题状态。
  • ThemeProvider.vue :提供了主题的状态、切换方法,并将它们通过 provide 传递给整个应用。
  • App.vue :根组件,在此处包裹 ThemeProvider 以便让整个应用都可以访问主题。
  • main.ts:入口文件,初始化 Vue 应用,并引入全局样式。

通过这个结构,你可以灵活地管理和切换主题,同时保持代码清晰和可维护。主题的切换效果是动态的,会实时反映在整个页面上。如果有其他需求或问题,随时告诉我!

多种主题设计

以下是一个完整可运行的 Vue 3 + TypeScript + Composition API 实现多主题切换的项目核心代码,支持 lightdarksolarizeddracula 四种主题:


✅ 1. src/theme-context.ts -- 定义主题上下文

typescript 复制代码
// src/theme-context.ts
import { InjectionKey, Ref } from 'vue'

export type Theme = 'light' | 'dark' | 'solarized' | 'dracula'

export interface ThemeContext {
  theme: Ref<Theme>
  toggleTheme: () => void
  setTheme: (t: Theme) => void
}

export const ThemeSymbol: InjectionKey<ThemeContext> = Symbol('ThemeContext')

✅ 2. src/assets/global.css -- 全局样式 + 多主题变量

css 复制代码
/* src/assets/global.css */

:root {
  --bg-color: #ffffff;
  --text-color: #333333;
  --button-bg: #4CAF50;
  --button-text-color: #ffffff;
  --card-bg: #f4f4f4;
  --link-color: #1E90FF;
}

html.dark {
  --bg-color: #121212;
  --text-color: #e0e0e0;
  --button-bg: #4CAF50;
  --button-text-color: #ffffff;
  --card-bg: #2c2c2c;
  --link-color: #1E90FF;
}

html.solarized {
  --bg-color: #fdf6e3;
  --text-color: #657b83;
  --button-bg: #268bd2;
  --button-text-color: #ffffff;
  --card-bg: #eee8d5;
  --link-color: #b58900;
}

html.dracula {
  --bg-color: #282a36;
  --text-color: #f8f8f2;
  --button-bg: #6272a4;
  --button-text-color: #ffffff;
  --card-bg: #44475a;
  --link-color: #8be9fd;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  font-family: 'Arial', sans-serif;
  transition: background-color 0.3s, color 0.3s;
}

button {
  background-color: var(--button-bg);
  color: var(--button-text-color);
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
}

.card {
  background-color: var(--card-bg);
  padding: 1rem;
  border-radius: 8px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}

✅ 3. src/components/ThemeProvider.vue -- 主题提供器

xml 复制代码
<!-- src/components/ThemeProvider.vue -->
<template>
  <div :class="theme">
    <slot />
  </div>
</template>

<script setup lang="ts">
import { ref, provide, watchEffect } from 'vue'
import { ThemeSymbol, Theme } from '../theme-context'

const theme = ref<Theme>('light')

const toggleTheme = () => {
  const themes: Theme[] = ['light', 'dark', 'solarized', 'dracula']
  const index = themes.indexOf(theme.value)
  theme.value = themes[(index + 1) % themes.length]
}

const setTheme = (t: Theme) => {
  theme.value = t
}

const stored = localStorage.getItem('theme')
if (stored && ['light', 'dark', 'solarized', 'dracula'].includes(stored)) {
  theme.value = stored as Theme
}

watchEffect(() => {
  localStorage.setItem('theme', theme.value)
  document.documentElement.className = theme.value
})

provide(ThemeSymbol, {
  theme,
  toggleTheme,
  setTheme,
})
</script>

✅ 4. src/components/ThemeSelector.vue -- 用户切换主题组件

xml 复制代码
<!-- src/components/ThemeSelector.vue -->
<template>
  <div class="card">
    <p>当前主题:{{ theme }}</p>
    <select v-model="selected" @change="setTheme(selected)">
      <option value="light">浅色</option>
      <option value="dark">深色</option>
      <option value="solarized">Solarized</option>
      <option value="dracula">Dracula</option>
    </select>
  </div>
</template>

<script setup lang="ts">
import { inject, ref, watch } from 'vue'
import { ThemeSymbol, Theme } from '../theme-context'

const themeContext = inject(ThemeSymbol)
if (!themeContext) throw new Error('Theme context not provided')

const { theme, setTheme } = themeContext
const selected = ref<Theme>(theme.value)

watch(theme, (val) => {
  selected.value = val
})
</script>

✅ 5. src/App.vue -- 应用根组件

xml 复制代码
<!-- src/App.vue -->
<template>
  <ThemeProvider>
    <ThemeSelector />
  </ThemeProvider>
</template>

<script setup lang="ts">
import ThemeProvider from './components/ThemeProvider.vue'
import ThemeSelector from './components/ThemeSelector.vue'
</script>

✅ 6. src/main.ts -- 应用入口

javascript 复制代码
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import './assets/global.css'

createApp(App).mount('#app')

✅ 使用方式总结

  • 启动后自动应用 localStorage 中保存的主题或默认 light。
  • <ThemeSelector /> 提供下拉菜单供用户选择主题。
  • CSS 变量基于 <html class="theme-name"> 实现不同主题风格。

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端