css modules 必须用 .module.css 后缀并 import styles from './x.module.css',否则静默退化为全局;文件名、导入写法、大小写、ts 类型声明任一出错均导致失效;devtools 中 class 无哈希即未启用;动态 classname 需用 clsx 避免 undefined;:global() 仅限必要穿透,不宜滥用。

必须用 .module.css(或 .module.scss 等)后缀命名文件,且导入时写成 import styles from './X.module.css';否则样式会静默退化为全局,根本起不到隔离作用。
文件名和导入写法错一点,CSS Modules 就完全失效
CSS Modules 不是开关,而是构建工具按文件名约定触发的机制。常见静默失败场景:
-
Button.css→ 全局注入,styles.button是undefined,但控制台不报错 -
Button.modules.css→ 多了个s,Webpack/Vite 不识别,等同于普通 CSS -
import './Button.module.css'→ 没解构出styles对象,类名不哈希,所有组件共用原始类名 -
import * as styles from './Button.module.css'→ 在 CRA 中返回空对象,styles.button仍是undefined
DevTools 里 class 没哈希,说明模块化根本没走通
打开浏览器开发者工具,检查元素的 class 属性:
- 看到类似
Button_button__Kx2f1这种带文件名和哈希的值 → 正常启用 - 还是原始
button或root→ 构建阶段压根没走 CSS Modules 流程
此时别调样式内容,先查三件事:文件后缀是否严格为 .module.css、import 路径是否大小写一致(macOS/Linux 敏感)、TypeScript 项目是否补了 declare module '*.module.css' 类型声明(否则 TS2307 报错)。
动态拼 className 时 styles.xxx 可能是 undefined
写 className={`${styles.root} ${styles[variant]}` 很危险——如果 variant 值在 CSS 文件里没定义,结果就是 "Button_root__abc undefined",React 渲染时把它当空字符串处理,样式直接丢失。
- 稳妥做法:用
clsx或对象写法,例如className={clsx(styles.root, styles[variant])} - 开发期可加
console.log(styles)确认导出内容;VS Code 插件(如 CSS Modules IntelliSense)能补全类名,避免手误 - 注意:
styles['btn-large']这种带短横线的类名必须用方括号访问,不能写styles.btn-large(语法错误)
:global() 是逃生舱,不是后门
它只应在必要时穿透模块边界,比如覆盖 Ant Design 的 .ant-modal 或重置基础盒模型:
- ✅ 合法:
:global(.ant-modal) { z-index: 9999; }、.container :global(.icon) { color: blue; } - ❌ 非法:
.btn { :global(.icon) { } }(嵌套写法不被解析) - ⚠️ 注意:
:global()内部的选择器不会哈希,原样输出到 DOM,和写全局 CSS 一样危险
真正容易被忽略的是:一旦开始大量用 :global() 或 composes 继承外部类,就说明设计已偏离 CSS Modules 的初衷——基础重置、工具类、CSS 变量这些该抽到 src/index.css 里统一引入,而不是靠模块文件硬扛。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











