BEM不是重命名运动,而是分阶段建立样式归属关系的过程:先锁定新增代码不恶化,再逐步收编高冲突模块;优先重构被多页面复用、类名泛化、依赖!important或DOM层级强压、易引发连锁样式的模块。

直接在旧项目里全局替换类名=制造线上事故。BEM不是重命名运动,是分阶段建立样式归属关系的过程——先锁住新增代码不恶化,再逐步收编高冲突模块。
怎么识别该优先重构的CSS模块
别从全局搜索 .btn 或 .card 开始。真正拖慢迭代的,是那些你改完一个地方、第二天收到三张样式bug工单的模块:
- 被至少两个以上页面或组件复用,但类名泛化(如
.list、.header) - 开发者常加
!important才能覆盖,或靠 DOM 层级强压(如.page .content .item a) - 上线后偶发「某个按钮变宽」「弹窗对齐错乱」,查半天发现是另一个模块的
.active泄露
这类模块就是迁移起点。先给它加 wrapper class,比如把旧 改成 ,后续所有新增子元素都走 main-nav__item、main-nav__toggle。
如何避免 BEM 命名踩坑:Block 名必须唯一且语义明确
Block 名不是“加前缀”那么简单。写成 card 还是 product-card,决定了后续三年有没有人敢动它:
-
card❌ —— 全局冲突高,别人写个.card__title时根本不知道你在哪个业务域 -
product-card✅ —— 明确归属,和user-card、cart-card天然隔离 -
card--featured✅ —— Modifier 表达视觉变体;card--loading❌ —— 这是 JS 状态,不该由 CSS 类控制 -
product-card__footer__copyright❌ —— 超过两级 element,说明copyright已具备独立 Block 特征,应拆为copyright-block
遇到历史遗留的 .clearfix、.hidden 这类工具类,统一迁移到 u-clearfix、u-hidden 前缀下,和 BEM 的业务类严格分离。
老 HTML 怎么不动结构就套上 BEM 类名
很多旧项目 DOM 层级深、嵌套乱,硬要按 BEM 拆元素会牵一发而动全身。更务实的做法是「语义映射」而非「结构重排」:
- 原 HTML:
- 不改结构,只加归属:
- 对应 CSS 删除所有孤立的
.input、.error规则,只保留.form-item__input和.form-item__error--visible - 禁止写
.form-item .input这类后代选择器——它破坏 BEM 的可预测性,也阻碍未来启用 CSS Modules
如果旧代码里大量依赖 div > span 这种结构选择器,先确认它是否真对应一个语义 Element;如果不是,改成 form-item__helper 或直接删掉。
团队协作时怎么守住 BEM 规范不退化
光靠文档和口头提醒没用。得让规范在开发流程里自动生效:
- 在 ESLint + Stylelint 配置中加入
stylelint-selector-bem-pattern,只对新写的.scss文件强制校验 - CI 流程里加检查:禁止提交含
.btn、.modal这类泛化类名的新 CSS 文件 - Vue/React 组件里,根元素 class 必须是 Block 名(如
article-card),子元素 class 必须带 Block 前缀,IDE 可配正则提示 - 第三方 UI 库(如 Ant Design)样式冲突?别用
!important盖,而是封装一层:内部用myapp-btn myapp-btn--primary,原始类名用all: unset隔离
最易被忽略的一点:BEM 不是终点,而是让「谁在改什么样式」这件事变得可追溯。哪怕只对 20% 的高频模块落地,也能拦住 80% 的样式连锁故障。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











