React官方脚手架开箱即用CSS Modules,必须满足三条件:文件名含.module.css后缀、import styles from方式引入、className={styles.xxx}动态绑定;缺一则退化为全局样式。

React 官方脚手架(Create React App)开箱即用支持 CSS Modules,但必须满足三个硬性条件:文件名带 .module. 后缀、import 方式引入、通过 styles.xxx 访问类名——缺一不可,否则就是全局样式。
文件名必须含 .module.css(或 .module.scss 等)
CRA 和 Vite 默认只对匹配 /\.module\.(css|scss|sass|less)$/ 的文件启用模块化。写成 Button.css 或 Button.modules.css 都无效。
-
✅ Button.module.css→ 正确,启用模块化 -
❌ Button.css→ 全局样式,无哈希,无作用域 -
❌ Button.modules.css→ 正则不匹配,等同于.css - 大小写敏感:Linux/macOS 下
button.module.css≠Button.module.css,后者导入时路径错就报Cannot find module
className={styles.xxx} 是唯一合法写法
CSS Modules 导出的是 JS 对象,键是原始类名,值是哈希后类名。它不支持字符串拼接、点号访问非法标识符、或绕过对象直接写字符串。
-
✅ className={styles.button}→ 正确 -
❌ className="button"→ 退化为全局样式,无隔离 -
❌ className={styles['btn-' + type]}→ 运行时可能为undefined,TS 不报错但 DOM 上 class="" -
❌ className={styles.button__icon}→ 语法错误(JS 中点号不能跟__),必须写styles['button__icon'] - 动态组合推荐用
clsx:className={clsx(styles.button, props.primary && styles.primary)}
混合使用模块类与全局类要分清来源
想用 animate.css 或 Tailwind 工具类?可以,但拼接时不能把全局类当模块对象属性用。
立即学习“前端免费学习笔记(深入)”;
✅ import styles from './Card.module.scss'; import '../../animate.scss';-
✅ className={`${styles.card} animate fadeIn`}→ 模块类走对象,全局类走纯字符串 -
❌ className={`${styles.card} .animate`}→ 多了个点,渲染为card_hash__abc .animate,浏览器忽略 -
❌ className={`${styles.card} 'animate'`}→ 单引号被当字面字符,渲染为card_hash__abc 'animate' - 全局样式必须被
import触发加载,否则即使写对类名也无效果
BEM 命名必须三者严格对齐
用 .card__header 不等于能写 styles.card__header——前提是文件名、CSS 类名、JSX 键名完全一致。
- 文件名必须是
Card.module.scss(PascalCase,与组件名同名) - CSS 里写
.card__header,不能写.Card__header或.card-header - JSX 中必须用
styles['card__header'],解构或点号访问都失败 - 调试时
console.log(styles)能直接看到键是否存在,比猜更可靠 - 开发期建议配
localIdentName=[name]__[local]___[hash:base64:5],编译后类名带原始名,一眼可查来源
最容易被忽略的其实是“安静失败”:CSS Modules 不会报错,styles.xxx 为 undefined 时只默默输出空字符串,DOM 上 class 属性消失,样式就没了——得靠 DevTools 看 class 值、看 console.log(styles)、再核对文件名/类名/引用三处是否逐字一致。


















