最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何借助 window.matchMedia 实现不依赖 CSS 的运行时深浅色皮肤逻辑分发
时间:2026-06-14 09:45:52 编辑:袖梨 来源:一聚教程网
不能完全绕过CSS,但可由JS主导主题决策并分发:先读取localStorage或系统偏好确定主题,再通过data-theme属性通知CSS生效,同时统一所有入口的判断逻辑以防闪跳。
不能完全绕过 CSS,但可以做到 JS 主导逻辑分发、CSS 仅负责样式呈现——关键在于把主题决策权收归 JS,不依赖 @media 自动匹配。
为什么不能“不依赖 CSS”?
深浅色皮肤最终要渲染到页面上,必然经过 CSS。所谓“不依赖 CSS”,实际是指:不靠 @media (prefers-color-scheme: dark) 自动触发样式切换,而是由 JS 明确决定当前该用哪套主题,并主动通知 CSS 生效。
-
window.matchMedia本身不改样式,只提供系统偏好信号 - 若想让
document.body变黑或变白,JS 必须操作 class、data-theme或 CSS 变量值 - CSS 里仍需定义
.dark { --bg: #121212; }这类规则,否则 JS 没东西可“分发”
window.matchMedia('(prefers-color-scheme: dark)') 的初始化与监听必须分离
很多人写成“监听一次就完事”,结果首屏错乱。正确做法是:先读取初始状态做一次分发,再监听后续变化。
- 立即执行
const mql = window.matchMedia('(prefers-color-scheme: dark)') - 用
mql.matches判断当前系统偏好,调用你的主题分发函数(如applyTheme('dark')) - 再用
mql.addEventListener('change', e => applyTheme(e.matches ? 'dark' : 'light')) - 务必在卸载时调用
mql.removeEventListener,否则监听器持续挂载,applyTheme可能被反复调用
手动覆盖优先级必须在 JS 层统一判断
用户点了“强制切亮色”,系统却是深色,此时 mql.matches 仍是 true,但你不该听它的。逻辑必须收口:
立即学习“前端免费学习笔记(深入)”;
- 读取
localStorage.getItem('color_mode'),若为'light'或'dark',直接采用 - 若为
null,才 fallback 到mql.matches - 每次主题变更(无论来自系统还是用户点击)都应同步写入
localStorage,保证刷新后仍保持选择 - 不要在 CSS 里用
@media和[data-theme]混用同一组变量名,否则书写顺序会意外覆盖优先级
分发目标建议用 data-theme 而非 class
用 document.documentElement.setAttribute('data-theme', 'dark') 比 classList.add('dark') 更可控:
-
data-theme是单一可信源,不会因其他脚本误删 class 导致主题丢失 - CSS 中可写
:root[data-theme="dark"] { --bg: #121212; },语义清晰且不影响第三方 class 命名空间 - 便于后续扩展(比如增加
auto状态,或服务端注入初始值) - 避免和 Tailwind 的
dark:前缀冲突——如果你用了dark:bg-gray-900,它底层仍依赖class="dark",那就得保持 class 方案,不能混用
真正容易被忽略的不是怎么写监听,而是「谁来决定当前主题」这个判断逻辑有没有被所有入口统一收口。一旦 localStorage、系统查询、URL 参数、服务端提示多个来源并存,又没在一个函数里做归一化,皮肤就会来回闪跳。