目录
01: 主题替换原理分析
02: TailWind DarkMode 原理
03: 为组件增加 Dark 适配
04: DarkMode 在复杂应用中的实现逻辑分析
05: DarkMode 在复杂应用中的实现
06: 跟随系统的主题变更
07: 总结
01: 主题替换原理分析
主题替换原理:通过类名来控制对应的样式(主题),当类名发生变化时,即完成了主题替换。
02: TailWind DarkMode 原理
// tailwind.config.js
module.exports = {
// 手动切换暗黑模式
darkMode: 'class'
……
}
// 受影响的元素
<div class="bg-white dark:bg-zinc-800"></div>
// 设置主题
<html class="dark"></html>
03: 为组件增加 Dark 适配
各个组件添加 " class='dark:bg-zinc-800 dark:hover:bg-zinc-300' " 。
04: DarkMode 在复杂应用中的实现逻辑分析
1. 监听主题的切换行为。
2. 根据行为,保存当前需要展示的主题到 vuex 中。
3. 根据 vuex 中保存的当前主题,展示 header-theme 下的显示图标。
4. 根据 vuex 中保存的当前主题,修改 html 的 class。
05: DarkMode 在复杂应用中的实现
- store
- - modules
- - - theme.js
- - getters.js
- - index.js
// src/store/modules/theme.js
import { THEME_LIGHT } from '@/constants'
export default {
namespaced: true,
state: () => ({
// 当前主题模式
themeType: THEME_LIGHT
}),
mutations: {
changeThemeType(state, newTheme) {
state.themeType = newTheme
}
}
}
// src/store/index.js
import theme from './modules/theme'
const store = createStore({
……
modules: {
theme,
}
……
}
// src/store/getters.js
export default {
themeType: (state) => state.theme.themeType
}
// src/views/layout/components/header/header-theme.vue
<script setup>
import { useStore } from 'vuex'
// 构建渲染数据源
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 store = useStore()
/**
* menu 切换事件
*/
const onItemClick = (themeItem) => {
store.commit('theme/changeThemeType', themeItem.type)
}
/**
* 控制图标展示
*/
const svgIconName = computed(() => {
const findTheme = themeArr.find((theme) => {
return theme.type === store.getters.themeType
}
return findTheme?.icon || themeArr[0].type
})
</script>
- utils
- - theme.js
// src/utils/theme.js
import { watch } from 'vue'
import store from '../store'
/**
* 初始化主题
*/
export default () => {
// 1. 当主题发生改变时,或者当进入系统时,可以进行 html class 的配置
watch(
() => store.getters.themeType,
(val) => {
// html 的 class
let themeClassName = ''
switch (val) {
case THEME_LIGHT:
themeClassName = 'light'
break
case THEME_DARK:
themeClassName = 'dark'
break
// 修改 html 的 class
document.querySelector('html').className = themeClassName
},
{
// 初始执行一次
immediate: true
}
)
}
// src/main.js
import useTheme from './utils/theme'
useTheme()
06: 跟随系统的主题变更
想要生成跟随系统的主题变更,那么我们就需要 **监听系统的主题变化**
想要做到这一点,可以利用 Window.matchMedia() 方法,该方法接受一个 mediaQueryString (媒体查询解析的字符串),该字符串我们可以传递 prefers-color-schema,即 window.matchMedia('(prefers-color-schema: dark)') 方法。
该方法可以返回一个 MediaQueryList 对象:
1. 该对象存在一个 change 事件,可以监听 主题发生变更 的行为。
2. 同时存在一个 matches 属性,该属性为 boolean 性的值:
1. true:深色主题
2. false:浅色主题
那么据此,可生成以下代码,在 src/utils/theme.js 中:
import store from '@/store'
import { watch } from 'vue'
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 = function () {
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 () => {
watch(() => store.getters.themeType, changeTheme, {
// 初始执行一次
immediate: true
})
}
07: 总结
主要讲解了 主题替换的功能,包含以下 4 个主要方面:
1. 主题替换原理
2. tailwind 主题替换原理
3. 复杂应用中的实现方案
4. 跟随系统的主体变更
目前市面上很多的 组件库 也都包含了主题替换的功能,它们的实现原理其实也就是本文章中所讲解的方式。
看完本文章之后,应该也可以对主题替换这样的普适功能有更加深入的了解啦~