用与 CSS 相同的媒体查询字符串在脚本中判断布局与系统偏好,并用 change 事件替代粗糙的 resize;附深色模式与减少动效示例及卸载监听要点。
window.matchMedia 是浏览器提供的标准 API,用来在脚本里判断当前环境是否满足某条 CSS 媒体查询,并在条件变化时收到通知。它和写在样式表里的 @media 使用同一套查询语法,因此可以把断点、深色模式、用户动效偏好等从「纯 CSS」延伸到组件逻辑或初始化策略中。
1. 基本用法
const mq = window.matchMedia("(min-width: 768px)");
// 当前是否匹配
console.log(mq.matches); // boolean
返回值是 MediaQueryList 实例,常用属性包括:
matches:此刻该查询是否为真。media:规范化后的查询字符串(便于调试时打印)。
查询写法与 CSS 一致,例如:
(min-width: 768px)、(max-width: 1023.98px)(prefers-color-scheme: dark)(prefers-reduced-motion: reduce)print、screen and (orientation: landscape)
权威文档可参考 MDN:Window.matchMedia。
2. 监听变化:比 window.resize 更贴切
若只为「是否超过某宽度」分支逻辑,用 resize 再手动算像素容易与 CSS 断点漂移,且触发频繁。matchMedia 的 change 事件只在该条查询的真假翻转时触发,语义与性能都更合适。
推荐(现代浏览器):
const mq = window.matchMedia("(min-width: 768px)");
function onChange(e) {
if (e.matches) {
// 满足 min-width: 768px
} else {
// 不满足
}
}
mq.addEventListener("change", onChange);
// 组件销毁或页面卸载时务必移除:
// mq.removeEventListener("change", onChange);
旧版 WebKit 等环境仍可能只实现 MediaQueryList.addListener(已标记为 legacy),可封装一层:优先 addEventListener("change"),不存在则回退 addListener。
3. 典型场景
3.1 与 CSS 断点对齐的组件逻辑
例如仅在「桌面」宽度初始化复杂图表,窄屏降级为列表:
const desktop = window.matchMedia("(min-width: 1024px)");
function applyLayout() {
if (desktop.matches) {
// initChart();
} else {
// destroyChart?.();
}
}
applyLayout();
desktop.addEventListener("change", applyLayout);
脚本里的 1024px 建议与样式里 @media (min-width: 1024px) 刻意共用同一常量或注释约定,避免两套断点慢慢不一致。
3.2 系统深色 / 浅色模式
const dark = window.matchMedia("(prefers-color-scheme: dark)");
function syncTheme(e) {
document.documentElement.dataset.theme = e.matches ? "dark" : "light";
}
syncTheme(dark);
dark.addEventListener("change", syncTheme);
若站点同时提供「用户手动选主题」,通常要在业务层约定优先级(例如用户选择优先于系统),matchMedia 只反映系统偏好,不会替你合并策略。
3.3 尊重「减少动效」
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
function shouldAnimate() {
return !reduceMotion.matches;
}
可与 CSS 中的 prefers-reduced-motion 搭配:样式侧关掉过渡与动画,脚本侧关闭轮播、视差等。
3.4 打印、横竖屏等
window.matchMedia("print").matches;
window.matchMedia("(orientation: portrait)").matches;
4. 服务端渲染与首屏
在 SSR(如 Astro、Next 等)中,构建或请求阶段没有 window,需要:
- 仅在浏览器环境调用(例如
typeof window !== "undefined"),或在框架的onMount/useEffect中调用; - 避免首屏 HTML 强依赖
matches的同步结果而导致闪烁,可与 CSS 变量、<meta name="color-scheme">或内联关键样式配合。
5. 常见误区
- 用轮询代替监听:不需要;应使用
change(或 legacy 的addListener)。 - 与
resize混用:同一套逻辑尽量单一数据源,宽度类需求优先媒体查询。 - 忘记注销监听:SPA 路由切换、组件卸载时移除监听,避免泄漏与重复绑定。
- 查询字符串写错:非法查询可能导致行为异常,开发时打印
mq.media并用开发者工具设备模拟验证。
6. 小结
| 能力 | 作用 |
|---|---|
matchMedia(query).matches | 同步读取当前是否满足查询 |
addEventListener("change", …) | 查询结果变化时回调(推荐) |
与 @media 同语法 | 断点、主题、无障碍偏好与样式层一致 |
将 matchMedia 用在「需要在 JS 里做决定」的场景(是否挂载重组件、是否启用动画、是否按打印样式采集 DOM 等),而把纯视觉仍交给 CSS,通常是最干净的分工方式。