BEM 在 React 中是组件作用域契约,需文件名(Button.module.css)、类名(.button__icon)、JSX 访问(styles['button__icon'])三者严格对齐;错用命名或访问方式将导致样式失效。

直接写 .button__icon 或拼字符串 `button ${variant ? 'button--' + variant : ''}` 是错的起点——BEM 在 React 里不是命名习惯,而是组件作用域契约,必须靠文件名、类名、JSX 访问三者对齐才能生效。
Button.module.css 文件名必须和组件名严格一致
CSS Modules 的哈希前缀由文件名决定:Button.module.css → 编译后类名以 Button_ 开头;button.module.css 或 MyButton.module.css 都会导致 styles.button__icon 永远为 undefined。
- React 组件叫
Button.tsx,CSS 文件就必须叫Button.module.css(PascalCase,无下划线、无中划线) - 文件内只允许声明以
.button开头的类:.button、.button__icon、.button--primary—— 写成.btn__icon或.Button__icon都不会映射进styles对象 - 验证方式:在浏览器开发者工具里看最终生成的类名,是否以
Button_button__开头;再console.log(styles)确认button__icon键是否存在
JSX 中访问 styles 必须用字符串键,不能点号或硬编码
styles.button__icon 是语法错误(非法标识符),styles['button__icon'] 才是正确路径——而且这个字符串必须和 CSS 文件中声明的类名逐字匹配,包括大小写、连字符、双下划线。
- 禁止:
className={styles.button__icon}(点号访问非法)、styles['Button__icon'](大小写不一致)、styles['button-icon'](少一个下划线) - 禁止混用:
`${styles.button} ${styles['button--primary']}`—— 后者为undefined时会拼出"button undefined",导致样式静默失效 - 推荐调试配置:
localIdentName=[name]__[local]___[hash:base64:5],编译后类名带原始名,一眼可查来源
动态拼接类名必须用 classnames + BEM 工厂函数
手写 {`button ${isActive ? 'button--active' : ''}`} 容易漏空格、拼错 modifier、传入 null 导致多余空白,还绕过 CSS Modules 的哈希校验。
立即学习“前端免费学习笔记(深入)”;
- 封装工厂:
const bem = (block) => ({ e: (el) => `${block}__${el}`, m: (mod) => `${block}--${mod}` }) - 绑定 block:
const buttonBem = bem('button') - 配合
classnames使用:className={cn(styles.button, buttonBem.m('primary'), { [styles[buttonBem.e('icon')]]: hasIcon })} - 禁止:
cn('button', { 'button--disabled': disabled })(字符串绕过校验)、cn(buttonBem(), `button__icon--${status}`)(插值破坏类型约束)
postcss-bem-linter 不是可选插件,是防滑坡底线
没有它,团队很快会写出 .button__icon--large--dark(嵌套 modifier)、.button .icon(空格后代选择器)、.button-icon(缺双下划线)这类反模式代码,破坏 Block 边界。
- 它能在构建阶段拦截:
.button__content--hover(Element 下不该挂交互态)、.modal__close(跨 Block 引用) - 它强制校验
__和--是否紧贴单词,避免.menu__ item这种空格错误(VS Code 插件不会报,但无效) - 真正卡住的是语义模糊:比如把
sidebar__btn当 Block,结果按钮在弹窗里复用时样式错乱——BEM 的 block 必须是独立、可复用的 UI 单元(如user-card),不是视觉容器


















