记录--Vue3自定义一个Hooks,实现一键换肤
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
核心
- 使用CSS变量, 准备两套CSS颜色, 一套是在 light模式下的颜色,一套是在dark模式下的颜色
- dark模式下的 CSS 权重要比 light 模式下的权重高, 不然当我们给html添加自定义属性
[data-theme='dark']
的时候, dark模式权重比light低,会一直不起效果 - 当我们点击 dark 模式的时候, 给 html 设置自定义属性
[data-theme='dark']
- 当我们点击 light 模式的时候, 给 html 设置自定义属性
[data-theme='light']
- 在 dark 模式下, 会匹配到
html[data-theme='dark']
选择器下的样式 - 在 light 模式下,由于我们没有设置
html[data-theme='light']
的方案, 那么他就匹配:root(即html)下的样式
两套样式代码大概如下(列了一部分):
1 2 3 4 5 6 7 8 9 10 11 | :root { --color-body-bg: #ffffff; --color-text: #000; --color-secondary-bg-for-transparent: rgba(209, 209, 214, 0.28); } html[data-theme='dark'] { --color-body-bg: #222222; --color-text: #ffffff; --color-primary-bg-for-transparent: rgba(255, 255, 255, 0.12); } |
1 2 | let theme = 'light' // light / dark document.documentElement.setAttribute('data-theme', theme) |
这样就能实现简单的更换肤色功能了
什么? 你以为这就完了?好戏刚开始
跟随系统颜色
首先利用Window
的 'matchMedia()' 方法返回一个新的 MediaQueryList
对象,表示指定的媒体查询 (en-US)字符串解析后的结果。
如运行媒体查询(max-width: 600px)
并在<span>
;中显示MediaQueryList
的matches
属性值。如果视口的宽度小于或等于 600 像素,则输出将为 true,而如果窗口的宽度大于此宽度,则将输出 false。
1 | < span class="mq-value"></ span > |
1 2 | let mql = window.matchMedia('(max-width: 600px)'); document.querySelector(".mq-value").innerText = mql.matches; //此时小于或等于600像素时span 里面的结果为false |
利用prefers-color-scheme
[CSS媒体特性] 用于检测用户是否有将系统的主题色设置为亮色或者暗色。
1 2 3 4 5 6 7 8 9 10 11 12 | .day { background: #eee; color: black; } .night { background: #333; color: white; } @media (prefers-color-scheme: dark) { .day.dark-scheme { background: #333; color: white; } .night.dark-scheme { background: black; color: #ddd; } } @media (prefers-color-scheme: light) { .day.light-scheme { background: white; color: #555; } .night.light-scheme { background: #eee; color: black; } } |
两者相结合
把matchMedia()
和prefers-color-scheme
结合在一起, 我们就可以通过 js 去给系统颜色为dark或 light 的情况下更换对应的 html自定义属性, 即[data-theme='dark']
或 [data-theme='light']
首先,我们先去获取主题颜色, 我们还没设置的时候,就默认是系统颜色, 设置了就把他存储起来,下次直接获取这个颜色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 获取主题变量 let appearance = ref< string >(localStorage.getItem('appearance') || 'auto') // 查询当前系统主题颜色 const match = window.matchMedia("(prefers-color-scheme: dark)") // 如果主题变量为 auto, 则跟随系统主题 if (appearance.value === 'auto') { followSystem() } else { document.documentElement.setAttribute('data-theme', appearance.value) } function followSystem() { // 当前系统颜色是亮色还是暗色 , 设置对应的html[data-theme= 'dark' 或者'light'] const theme = match.matches ? 'dark' : 'light' document.documentElement.setAttribute('data-theme', theme) } // 监听系统主题变化,电脑主题发生改变的时候就调用followSystem函数 match.addEventListener('change', followSystem) |
封装成一个hooks
暴露出一个 useThemeColor函数, 返回一个对象, 对象里面返回我们的主题变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | / 获取主题变量 let appearance = ref< string >(localStorage.getItem('appearance') || 'auto') // 查询当前系统主题颜色 const match:MediaQueryList = window.matchMedia("(prefers-color-scheme: dark)") // 监听系统主题变化 match.addEventListener('change', followSystem) function followSystem() { const theme = match.matches ? 'dark' : 'light' document.documentElement.setAttribute('data-theme', theme) } watchEffect(() => { // 如果主题变量为 auto, 则跟随系统主题 if (appearance.value === 'auto') { followSystem() } else { document.documentElement.setAttribute('data-theme', appearance.value) } }) export default function useThemeColor() { return { appearance, } } |
使用hooks
导入我们export出来的函数
1 | import useThemeColor from '../hooks/useThemeColor' |
使用函数,注意, 这里返回的 apprance 已经是一个响应式数据了
1 | const { appearance } = useThemeColor() |
使用 v-model 绑定apprance,直接使用apprance , 当我们切换颜色的时候, 就会调用watchEffect里面的函数, 达到一键换肤效果
1 2 3 4 5 6 7 8 9 10 11 12 | < div class="item"> < div class="left"> < div class="title">外观</ div > </ div > < div class="right"> < select v-model="appearance"> < option value="auto">{{ "自动" }}</ option > < option value="light">🌞 {{ "浅色" }}</ option > < option value="dark">🌚 {{ "深色" }}</ option > </ select > </ div > </ div > |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)