CSS Modules 本身不提供响应式能力,必须配合 @media 查询实现;媒体查询需写在 .module.css 文件中,由 CSS 规则驱动,而非 JS 控制类名切换。

直接用 CSS Modules 做响应式布局是可行的,但必须配合媒体查询(@media)——CSS Modules 本身只解决作用域隔离,不提供响应能力;响应逻辑仍靠 CSS 规则本身驱动。
为什么不能只靠 className 或 styleName 自动响应?
CSS Modules 的核心行为是将类名哈希化(如 sidebar__toggle___1x2aB),它不修改、不增强、也不拦截你写的媒体查询规则。也就是说:@media (max-width: 768px) 这类声明照常生效,只要它们写在 .module.css 文件里,就会被正确注入并作用于对应组件。
常见误解是以为启用了 CSS Modules 就“自动支持响应式”,其实只是避免了类名污染,响应逻辑还得自己写。
@media 必须写在 .module.css 文件里,不能只靠 JS 切换类名
如果你把断点逻辑挪到 JS 层(比如用 useState 根据 window.innerWidth 动态切换类名),会丢失 SSR 支持、触发水合不一致、且难以维护。正确做法是把所有断点样式留在 CSS 中:
立即学习“前端免费学习笔记(深入)”;
- 在
Layout.module.css中直接写@media (max-width: 768px) { .container { flex-direction: column; } } - 确保该文件被组件
import styles from './Layout.module.css'引入 - 使用
className={styles.container}—— 不要用styleName(那是react-css-modules的旧 API,已不推荐) - 不要在 JS 中拼接字符串类名(如
className={`container ${isMobile ? 'mobile' : ''}`>),这绕过了 CSS Modules 的作用域保护
移动端优先 + Flexbox 是最简可靠组合
React 组件中不需要额外封装逻辑,纯靠 CSS 实现折叠/堆叠/隐藏即可。例如侧边栏在小屏收起、主内容占满宽度:
.sidebar {
width: 240px;
}
.main {
flex: 1;
}
@media (max-width: 768px) {
.sidebar {
display: none;
}
.main {
margin-left: 0;
}
}
注意两点:
-
display: none比visibility: hidden更彻底,避免占位和焦点问题 - 别依赖
min-width覆盖默认样式——移动端优先意味着默认写小屏样式,再用@media (min-width: 769px)向上增强 - 如果用
flex布局,确保父容器设display: flex且子元素没写死width: 100%,否则媒体查询可能被内联宽覆盖
容易忽略的 SSR 和构建陷阱
开发时看着正常,上线后响应失效?大概率是构建或服务环节漏了关键点:
- Webpack 配置中
css-loader的modules选项必须开启({ modules: true }),否则.module.css会被当普通 CSS 处理,类名不哈希,媒体查询虽生效但可能全局冲突 - Vite 用户需确认
css.modules.generateScopedName已配置(默认已开),且未误加?raw后缀导致样式未注入 - 服务端渲染时,若用
renderToStaticMarkup而非renderToString,CSS 注入逻辑可能被跳过,导致首屏无响应样式 - 媒体查询中的单位尽量用
px(如768px),避免用em或rem导致断点漂移(尤其在用户缩放页面时)
真正难的不是写几条 @media,而是让这些规则在服务端生成、客户端水合、构建产物中都保持一致作用域和生效顺序。


















