CSS Modules 通过文件名(如 Button.module.css)触发,必须用 import styles from './Component.module.css' 导入并使用 styles.xxx 类名,否则样式全局生效或失效。

直接用 import styles from './Component.module.css',不是 import './Component.module.css' —— 后者是全局样式,前者才是模块化。
文件名必须带 .module.css 后缀
CSS Modules 不是开关选项,而是构建工具(如 Webpack、Vite)按文件名约定触发的机制。只要文件名不匹配,就完全不会启用模块化,也不会报错,样式会静默变成全局注入。
-
Button.module.css✅ 正确,被识别为模块 -
Button.css❌ 全局样式,类名不哈希 -
Button.modules.css❌ 拼写错误,等同于.css -
Button.scss或Button.module.scss✅ 支持,但需对应 loader 配置(如 sass-loader)
导入必须解构为对象,不能只 import
import './Button.module.css' 这种写法看似省事,但结果是:样式生效了,但类名没哈希,所有组件共享同一套原始类名,等于没开 CSS Modules。
- ✅ 正确写法:
import styles from './Button.module.css',然后用className={styles.btn} - ❌ 错误写法:
import './Button.module.css'或import * as styles from './Button.module.css'(后者在 CRA 中返回空对象) - ⚠️ 注意:
styles是个普通 JS 对象,键是原始类名,值是生成的哈希字符串(如Button_btn__kx9f2),不能当字符串直接拼接
动态类名要小心 styles 不存在的风险
用 clsx 或三元表达式拼类名时,如果传入了未定义的 styles.xxx,React 渲染会把 undefined 当作空字符串,最终 className 变成 undefined,导致样式丢失且无提示。
- ✅ 安全写法:
className={clsx(styles.btn, condition && styles.primary)} - ✅ 更稳妥:
className={clsx(styles.btn, condition ? styles.primary : null)} - ❌ 危险写法:
className={`${styles.btn} ${styles.primary}`}—— 若styles.primary不存在,会得到"Button_btn__kx9f2 undefined" - ? 提示:开发时可加
console.log(styles)确认导出内容;VS Code 插件(如 CSS Modules IntelliSense)能补全类名
别让 :global() 破坏模块边界
:global() 是逃生舱,不是后门。它只应在必要时穿透作用域,比如重置第三方组件 margin,或对接 legacy CSS。滥用会导致模块化形同虚设。
- ✅ 合法:
:global(.ant-btn) { margin: 0; }或.container :global(.icon) { color: blue; } - ❌ 非法:
.btn { :global(.icon) { ... } }—— 嵌套写法不被解析,编译后无效 - ⚠️ 注意:
:global()内部的选择器不会哈希,会原样输出到 DOM,和全局 CSS 一样危险 - ? 替代方案:优先用
composes复用其他模块类,或抽离基础样式到非模块化的base.css
真正容易被忽略的是导入顺序和 HMR 行为:修改 import 语句顺序(比如调换两个 .module.css 的位置),会导致哈希类名重算,热更新后旧类名还在 DOM 里,新类名没匹配上,样式瞬间消失。这不是 bug,是哈希依赖导入顺序的必然结果。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











