BEM中必须用btn--primary而非btn-primary或btn_primary,因为--是唯一合法修饰符分隔符,确保工具链能准确识别状态语义、避免误删和协作歧义;动态类名须用clsx等工具枚举控制,禁止字符串拼接与模糊匹配。

直接用 block--modifier 形式声明变体,不拼字符串、不靠选择器“碰巧匹配”,才能真正复用和可维护。
为什么不能写 btn-primary 或 btn_primary
BEM 规范中,-- 是修饰符唯一合法分隔符。它不是为了好看,而是让工具链(如 PurgeCSS、TypeScript 类型推导、VS Code 插件)能准确识别「这是块的状态」而非新组件。
-
btn--primary✅ 被解析为btn块的primary变体,CSS 选择器可统一收口,构建时不会误删 -
btn-primary❌ 被当作独立块名,DevTools 里看不出语义关联,团队协作时容易重复造轮子 -
btn_primary❌ 下划线无 BEM 约定,PostCSS 插件无法提取合法修饰符枚举值
React/Vue 中传 variant 时怎么避免 class 拼接失控
别写 className={`btn btn--${variant}`} —— 一旦叠加 size、disabled、loading,字符串就变成不可读、不可校验的垃圾。
- 用
clsx或class-variance-authority这类工具做条件合并:clsx('btn', { 'btn--primary': variant === 'primary', 'btn--ghost': variant === 'ghost' }) - 所有合法值必须提前枚举,比如
type Variant = 'primary' | 'ghost' | 'outline',配合as const防止传错拼写 - 禁止在组件内动态生成修饰符名,如
`btn--${size}-${theme}`—— 这会让 CSS 文件膨胀且无法做静态分析
CSS 里怎么写修饰符才不掉坑
每个修饰符只负责自己那部分样式,不覆盖其他状态,也不依赖组合顺序。
立即学习“前端免费学习笔记(深入)”;
- 单独定义:
.btn.btn--disabled { opacity: 0.6; }和.btn.btn--loading { cursor: wait; }分开写,不要合并在一个选择器里 - 禁止写
.btn.btn--disabled.btn--loading这种组合选择器 —— 多个状态共存时极易漏类、难调试、权重混乱 - 基础块必须设
overflow: hidden(尤其头像类组件),否则avatar--circle之类修饰符会因图片溢出失效 - 避免在修饰符里塞性能敏感属性,比如
btn--loading { filter: blur(1px); }—— 高频切换时掉帧明显
最容易被忽略的是:修饰符不是“多写几个 class”那么简单,它是把组件的合法状态显式契约化。一旦允许运行时任意字符串注入,或者靠 [class*="--"] 这类模糊匹配兜底,BEM 就退化成带连字符的普通命名而已。


















