目录
01: 主题替换原理分析
02: TailWind DarkMode 原理
03: 为组件增加 Dark 适配
04: DarkMode 在复杂应用中的实现逻辑分析
05: DarkMode 在复杂应用中的实现
06: 跟随系统的主题变更
07: 总结
01: 主题替换原理分析
主题替换原理:通过类名来控制对应的样式(主题),当类名发生变化时,即完成了主题替换。
02: TailWind DarkMode 原理
// tailwind.config.jsmodule.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.jsimport { THEME_LIGHT } from '@/constants'export default {namespaced: true,state: () => ({// 当前主题模式themeType: THEME_LIGHT}),mutations: {changeThemeType(state, newTheme) {state.themeType = newTheme}}
}
// src/store/index.jsimport theme from './modules/theme'const store = createStore({……modules: {theme,}……
}
// src/store/getters.jsexport 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.jsimport { watch } from 'vue'
import store from '../store'/*** 初始化主题*/
export default () => {// 1. 当主题发生改变时,或者当进入系统时,可以进行 html class 的配置watch(() => store.getters.themeType,(val) => {// html 的 classlet themeClassName = ''switch (val) {case THEME_LIGHT:themeClassName = 'light'breakcase THEME_DARK:themeClassName = 'dark'break// 修改 html 的 classdocument.querySelector('html').className = themeClassName},{// 初始执行一次immediate: true})
}
// src/main.jsimport 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) returnmatchMedia = window.matchMedia('(prefers-color-scheme: dark)')// 监听主题变更matchMedia.onchange = function () {changeTheme(THEME_SYSTEM)}
}/*** 变更主题* @param {*} theme 主题的标记常量*/
const changeTheme = (theme) => {// html 的 classlet themeClassName = ''switch (theme) {case THEME_LIGHT:themeClassName = 'light'breakcase THEME_DARK:themeClassName = 'dark'breakcase THEME_SYSTEM:watchSystemThemeChange()themeClassName = matchMedia.matches ? 'dark' : 'light'break}// 修改 html 的 classdocument.querySelector('html').className = themeClassName
}/*** 初始化主题*/
export default () => {watch(() => store.getters.themeType, changeTheme, {// 初始执行一次immediate: true})
}
07: 总结
主要讲解了 主题替换的功能,包含以下 4 个主要方面:
1. 主题替换原理
2. tailwind 主题替换原理
3. 复杂应用中的实现方案
4. 跟随系统的主体变更
目前市面上很多的 组件库 也都包含了主题替换的功能,它们的实现原理其实也就是本文章中所讲解的方式。
看完本文章之后,应该也可以对主题替换这样的普适功能有更加深入的了解啦~