先记住这个答案
布局和配色若只影响呈现,用 CSS 媒体查询就能让浏览器自行切换。若断点还决定是否启动高成本交互或加载资源,脚本可通过 matchMedia() 读取当前匹配状态并监听变化。只在页面加载时读一次会漏掉窗口缩放;把纯视觉变化搬进脚本则会多出状态同步与清理负担。
- 纯视觉变化优先写 CSS
matchMedia可读当前匹配状态change只在匹配状态变化时触发- 组件卸载时移除监听
地图面板跨断点时同步行为
地图组件的图层信息用 <details id="map-panel"> 显示,窄屏时要关掉默认展开的面板。这涉及控件的打开状态,不是简单的样式切换。初始化时用 matchMedia 读一次;之后监听 change,当视口跨过断点再调整面板行为。不要只在页面加载时检查一次,否则旋转屏幕或调整窗口后,行为和布局会分离。服务端渲染时 window 不存在,访问应放在客户端生命周期里。
const panel = document.querySelector('#map-panel')
const query = window.matchMedia('(max-width: 600px)')
const sync = () => {
panel.open = !query.matches // 窄屏关闭详情面板
}
sync()
query.addEventListener('change', sync)
// 组件卸载时:query.removeEventListener('change', sync)matchMedia 使用和 CSS 媒体查询一致的表达式,但它返回的是查询对象,不会替你修改样式。
这段代码假定页面有一个 <details id="map-panel">。它先根据当前断点设置 open,再在匹配状态跨过 600px 时更新;只在 resize 中读窗口宽度,反而会在每一帧触发不必要的同步。若产品允许用户手动展开面板,还要决定自动断点切换是否可以覆盖用户选择,不能让脚本一运行就把刚打开的详情关掉。
matches 与 change 分工
matches 是当前结果;change 事件只在匹配状态变化时触发,并不等于每一次 resize 都触发。开发时要考虑生命周期和初始状态,尤其在 React 等框架里,避免每次渲染都注册一个新监听器。对于组件容器的尺寸变化,视口媒体查询不够精准,可考虑 CSS 容器查询或 ResizeObserver。
例如地图组件在窄屏时不只是缩小宽度,而要暂停高频拖拽提示或切换控制面板行为,这才需要脚本知道匹配状态。初始化先读一次 matches,之后监听 change,卸载时移除监听,避免路由切换后旧回调仍改当前页面。若只是侧栏从左移到下方,CSS 能完成,额外保存一份 JavaScript 状态反而容易不同步。
容易答错的地方
- 只在初始化时读一次
matches - 视口或偏好变化后值可能更新,需要变化时同步业务状态。
- 用
matchMedia重写所有响应式 CSS - 它不会替代 CSS 级联;纯布局交给样式规则更简单。
面试官还会怎么问?
change 事件何时触发?
查询的匹配布尔值发生变化时触发,不是每一次窗口尺寸变化都触发。
SSR 中怎样避免报错?
只在客户端生命周期访问 window,并为服务端初始渲染准备稳定的默认状态。
监听组件宽度该用什么?
媒体查询看视口或环境;组件自身宽度优先考虑容器查询,需要脚本尺寸数据再用 ResizeObserver。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。