保留可读性的关键是文件名、类名、JSX访问键严格对齐:Button.tsx配Button.module.css,仅含.button开头类,JSX用styles['button__icon']访问;注释分区、禁嵌套、用clsx+BEM工厂、postcss-bem-linter校验。

开了 CSS Modules 之后,.button__icon 编译成 _button__icon_abc123,机器认得,人看不懂——保留可读性的关键不是“不让它哈希”,而是让原始 BEM 名在开发链路中全程可追溯、可定位、可协作。
文件名 + 类名 + JSX 访问键必须严格对齐
这是最常踩的坑,也是唯一能保证 styles['button__icon'] 不为 undefined 的前提:
- React 组件叫
Button.tsx,CSS 文件就必须是Button.module.css(PascalCase,无下划线、无前缀) -
Button.module.css里只允许出现以.button开头的类:✅.button、.button__icon、.button--primary;❌.btn__icon、.Button__icon、.button-icon - JSX 中必须用字符串访问:
styles['button__icon']✅,styles.button__icon❌(语法错误),styles['Button__icon']❌(大小写不匹配) - 验证方式很简单:
console.log(styles)看输出对象里有没有button__icon这个 key;再打开 DevTools 查编译后类名是否以Button_button__开头
用注释分区让 .module.css 文件一眼可读
BEM 类名堆在一起就失去结构感,加注释不是“装饰”,是给编辑器和人眼提供折叠锚点:
- 每个文件按
BLOCK/ELEMENTS/MODIFIERS三段划分,用统一格式注释:/* ========================================================================== BLOCK: button ========================================================================== */ - 每段之间空一行,区块内类名保持平铺(禁止 Sass 嵌套生成,否则注释失效)
- Element 类只放
ELEMENTS区,Modifier 类只放MODIFIERS区——比如.button__label--highlighted必须归到 MODIFIERS,不能混进 ELEMENTS - 配置 VS Code 或 WebStorm 按
/* ===折叠代码,Ctrl+F 搜BLOCK:就跳到模块入口
动态组合类名时别绕过 styles 对象
手写字符串拼接 `button ${isActive ? 'button--active' : ''}` 看似简单,实际等于放弃 CSS Modules 的全部优势:
立即学习“前端免费学习笔记(深入)”;
- 字符串里的
button--active不经过styles映射,构建后可能根本没被引入,样式静默失效 - TypeScript 无法校验拼写,
button--primar这种错不会报错,只在页面上漏样式 - 推荐用
clsx+ BEM 工厂函数:const b = bem('button'),然后clsx(styles.button, styles[b.m('primary')], { [styles[b.e('icon')]]: hasIcon }) - 工厂函数返回的是原始 BEM 字符串(如
'button__icon'),再通过styles[...]查表,既保语义又保哈希校验
postcss-bem-linter 是底线,不是加分项
它能在构建阶段拦截所有破坏 BEM 结构的写法,比人工 Code Review 更可靠:
- 会报错:
.button .icon(后代选择器)、.button-icon(缺双下划线)、.button__content--loading(Element 下挂 Modifier) - 不报错但警告:
.button--primary--large(多个 modifier 是否正交需人工确认) - 配合
stylelint-selector-bem-pattern可在保存时就提示,不用等构建失败才发现 - 小团队可以先用 ESLint 插件起步,但一旦组件数 > 50,必须上
postcss-bem-linter,否则结构滑坡不可逆
真正容易被忽略的不是怎么写 .button__icon,而是忘了 BEM 的核心价值不在 CSS 里——而在文件命名、目录结构、JSX 类名访问这三者的映射一致性。只要断了一环,可读性就塌一半。


















