block必须是语义清晰、跨页面复用、职责独立的ui单元,如user-card、breadcrumb;element仅一级且不可嵌套,如card__title;modifier须表达稳定状态,如--disabled,禁用视觉描述。

后台页面哪些区域必须定义为 Block
不是所有视觉容器都能当 Block。真正该拆的,是语义清晰、跨页面复用、职责独立的 UI 单元:user-card、permission-tree、log-table、breadcrumb、search-form。它们出现在用户列表页、弹窗、导出预览页时,类名不变——这才是 Block 的价值。
常见错误是把 section-2、wrap、container 这类泛义名当 Block,结果改一处样式,三处页面全崩。更危险的是把 nav 或 ol 这类 HTML 标签直接当 Block 名,它只描述结构,不表达业务意图。
-
user-card可以独立存在,内部有头像、姓名、状态标签,它就是 Block - 表格行里的操作按钮不是
log-table__action-btn,而是复用独立的action-buttonBlock,再用user-card__action表达组合关系 - 面包屑整体是
breadcrumbBlock,不是把nav当 Block —— 后者是容器,前者才是组件
Element 为什么只能一级、不能嵌套
card__header__title 看起来“很合理”,但它是 BEM 最典型的结构性错误:违反「元素只属于一个 Block」原则,破坏作用域隔离。浏览器不报错,但你查样式时根本不知道这个类是哪个组件的,复用时极易冲突。
正确做法只有两种:card__title(标题直属于 card),或抽成新 Block heading(如果标题在 modal、sidebar、card 多处复用)。
- SCSS 中只允许单层嵌套:
.card { &__title {} &__body {} },禁止&__header { &__title {} } -
user-list__item__avatar是错的;应写user-list__item+avatar(独立 Block)或user-list__item-avatar(作为 item 的子元素,仍属 user-list 范畴) - Element 名必须是名词,且不可脱离 Block 存在:
menu__item✅,logo❌(若多处复用,它自己就是 Block)
Modifier 怎么写才不变成“伪状态”
button--primary--disabled 合法,但 button--blue 或 card--top-margin 就踩坑了。Modifier 必须表达稳定、可预期的状态,比如 --disabled、--loading、--collapsed,而不是视觉描述或布局细节。
颜色、间距、位置这些,应该由主题系统或 CSS 自定义属性控制,而非塞进 Modifier。否则换个主题就得批量改 class 名,协作成本爆炸。
- 当前页状态用
breadcrumb__item--current,不是靠:last-child伪类动态推断 - 折叠菜单用
permission-tree--collapsed,不是permission-tree--small(尺寸是视觉表现,不是状态) - 修饰符必须依附于 Block 或 Element,不能单独存在:
button--primary✅,button__text--primary❌(text 不该决定按钮主色)
如何避免 BEM 在真实项目里“形似神散”
BEM 最容易失效的地方,不是命名拼错,而是边界模糊时妥协:看到一个图标在多个地方出现,就随手写 icon,结果发现它在 card 里要小一圈、在 sidebar 里要带边框——这时该加 icon--in-card,而不是改成 card__icon。
工具链比文档更可靠。纯靠人肉约定,大项目三个月后必乱。Webpack 配 postcss-bem,Vue/React 用 CSS Modules 或 data-bem 属性辅助调试,浏览器里搜 [data-bem] 能立刻定位组件边界。
- 禁止用标签选择器写样式:
div.user-card❌,.user-card✅ - 每个 Block 对应一个独立 CSS 文件(如
user-card.css),里面只写user-card相关规则 - 响应式 Modifier 要写成
user-card user-card--mobile,不是user-card--mobile单独存在,否则媒体查询失效
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











