Open tiantingrui opened 1 year ago
Dark Mode: https://tailwindcss.com/docs/dark-mode
- 给对应DOM 增加
dark:
- 给 html 增加
dark
配置 tailwind.config.cjs
module.exports = {
// 手动切换暗黑模式
darkMode: 'class',
}
import { defineStore } from 'pinia'
import { THEME_LIGHT } from '@/constants'
export const useThemeStore = defineStore('theme', {
state: () => ({
theme: THEME_LIGHT // 初始值
}),
getters: {
themeType: (state) => state.theme
},
actions: {
changeTheme(newTheme) {
this.theme = newTheme
}
},
persist: {
storage: sessionStorage,
paths: ['theme']
}
})
// header-theme.vue
<script setup>
import { THEME_LIGHT, THEME_DARK, THEME_SYSTEM } from '@/constants'
import { useThemeStore } from '@/store'
import { computed } from 'vue'
const themeArr = [
{
id: '0',
type: THEME_LIGHT,
icon: 'theme-light',
name: '极简白'
},
{
id: '1',
type: THEME_DARK,
icon: 'theme-dark',
name: '极夜黑'
},
{
id: '2',
type: THEME_SYSTEM,
icon: 'theme-system',
name: '跟随系统'
}
]
const themeStore = useThemeStore()
/**
* 展示图标
*/
const svgIcon = computed(() => {
const findTheme = themeArr.find((theme) => {
return theme.type === themeStore.themeType
})
return findTheme?.icon || themeArr[0].icon
})
/**
* 切换主题
* @param {*} item
*/
const onChangeTheme = (item) => {
themeStore.changeTheme(item.type)
}
</script>
// src/utils/theme.js
/**
* 根据 pinia中保存的当前主题,修改 html的class
*/
import { watch } from 'vue'
import { useThemeStore } from '@/store/theme'
import { THEME_LIGHT, THEME_DARK, THEME_SYSTEM } from '@/constants'
/**
* 监听系统主题变更
*/
let matchMedia
const watchSystemThemeChange = () => {
// 仅需一次初始化
if (matchMedia) return
matchMedia = window.matchMedia('(prefers-color-scheme: dark)')
// 监听主题变化
matchMedia.onchange = () => {
changeTheme(THEME_SYSTEM)
}
}
/**
* 变更主题
* @param {*} theme 主题的编辑
*/
const changeTheme = (theme) => {
// html 的 class
let themeClassName = ''
switch (theme) {
case THEME_LIGHT:
themeClassName = 'light'
break
case THEME_DARK:
themeClassName = 'dark'
break
case THEME_SYSTEM:
// 调用方法,监听系统主题变化
watchSystemThemeChange()
themeClassName = matchMedia.matches ? 'dark' : 'light'
break
}
// 修改 html的 class
document.querySelector('html').className = themeClassName
}
/**
* 初始化主题
*/
export default () => {
// 1. 当主题发生改变时,或者当进入系统时,可以进行 html class的配置
const themeStore = useThemeStore()
watch(() => themeStore.theme, changeTheme, {
// 初始执行一次
immediate: true
})
}
重点: 监听系统的主题变化
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/matchMedia https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/prefers-color-scheme
想要做到这点,可以利用 window.matchMedia
方法。该方法接收一个 mediaQueryString
(媒体查询解析的字符串),该字符串我们可以传递 prefers-color-scheme
,即 window.matchMedia('(prefers-color-scheme: dark)')
方法
该方法返回一个 MediaQueryList
对象:
change
事件,可以监听主题发生变更
的行为matches
属性,该属性为 boolean
值
代码实现 /src/utils/theme.js
(上条 comment )
最后在 main.js
中引入
坑:
一定记住 app.use(pinia) 在 useTheme() 之前调用,以防拿不到 pinia 创建的实例。
// main.js
import useTheme from './utils/theme'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const app = createApp(App)
app.use(router)
app.use(pinia)
// 初始化主题
useTheme()
app.use(mLibs)
app.mount('#app')
主题替换原理
通过类名来控制对应的样式(主题),当 类名发生变化时,即完成了主题替换