BEM不是CSS-in-JS的替代方案,而是补充工具;它仅规范命名结构,不提供运行时样式、作用域封装、Tree-shaking或类型安全等能力。

BEM 不是 CSS-in-JS 的“替代方案”,它压根不解决同一类问题——CSS-in-JS 解决的是运行时样式注入、动态主题、组件级作用域封装;BEM 只负责命名和结构约定,连作用域都不管。 你拿它去“替代” CSS-in-JS,就像用螺丝刀拧灯泡:能勉强转两下,但根本没对准接口。
为什么很多人误以为 BEM 能替代 CSS-in-JS
常见错误现象:useTheme 返回的 theme.colors.primary 在 JSX 里写成 className="button--primary",就以为“不用 CSS-in-JS 也能动态换色”。其实这只是把状态映射逻辑从 JS 搬到了 class 名拼接里,button--primary 还得在 CSS 文件里硬写死颜色值,改主题仍要改多处 CSS。
真正混淆点在于:BEM 确实能提升可维护性,但它不提供以下能力:
- 运行时样式计算(比如根据 props 动态生成
padding: ${props.size * 4}px) - 组件级 CSS 作用域(BEM 类名仍是全局注册的,
.modal__overlay冲突了照样覆盖) - Tree-shaking 支持(BEM 写的 CSS 全部打进 bundle,哪怕某个 block 根本没用)
- 类型安全(
button--large拼错成button--largo,编译器不报错)
BEM 在 CSS-in-JS 项目中该扮演什么角色
它不是“替代”,而是“补充”——给 CSS-in-JS 的字符串类名加语义锚点。比如在 Emotion 或 Styled Components 中,你仍会写:
const Button = styled.button`
&.${styles.button} {
padding: 0.5rem 1rem;
}
&.${styles.button}--disabled {
opacity: 0.6;
}
`;
这里的 styles.button 是从 Button.module.css import 来的,或由 BEM 命名规则生成的常量对象。这样做的好处:
- 搜索
button--disabled能直接定位到样式定义和所有使用位置 - 重构时 rename
buttonblock,所有相关类名自动更新(配合 IDE 支持) - 避免
css`opacity: ${disabled ? 0.6 : 1}`这种纯内联写法导致的样式不可复用
注意:别在 css 模板字面量里手写 .button__icon--small —— 那等于放弃 CSS-in-JS 的变量/函数能力,退化回手写 CSS。
BEM + CSS Modules 才是真·轻量替代方案
如果你明确想避开 CSS-in-JS 的 runtime 开销和 bundle 体积,CSS Modules 加 BEM 命名是最务实的选择:
-
Button.module.css里写.button {} .button__icon {} .button--loading {} - JSX 中用
className={styles.button},构建时自动转成唯一哈希类名 - 权重天然可控(全是类选择器,无嵌套依赖),
!important几乎绝迹 - 支持
@layer components(现代 PostCSS),和 Tailwind 共存也不打架
性能影响:零 runtime 成本,仅增加构建时几毫秒哈希计算;工程化收益:类名可搜索、可 lint、可生成文档。
真正容易被忽略的点是:BEM 的价值不在“要不要用”,而在“谁来保证它被正确执行”。没人 review header__logo--dark 是否真对应一个 dark 状态,而只是个视觉微调,那它就只是又一个自欺欺人的类名。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











