BEM是分阶段建立样式归属关系的协作契约,非全局重命名工具;应优先重构被多页面复用、无上下文类名(如.nav)、频繁需!important或深层选择器覆盖、引发JS逻辑断裂的模块,而非泛化名.header/.list。

直接全局搜索替换 .btn 或 .card 类名 = 线上样式大面积崩坏,JS 逻辑断裂,且无法回滚。BEM 不是重命名工具,而是分阶段建立样式归属关系的协作契约。
哪些模块该优先重构,而不是从全局搜索开始
别碰 .header、.list 这类泛化名。真正该动的是那些你改完一个地方,第二天收到三张样式 bug 工单的模块:
- 被至少两个以上页面或组件复用,但类名无上下文(如
.nav在登录页和后台都用) - 开发者频繁加
!important或靠深层后代选择器(如.page .content .item a)才能覆盖 - 上线后偶发「弹窗按钮变宽」「侧边栏对齐错乱」,最后发现是另一个模块的
.active泄露覆盖 - JS 里大量出现
document.querySelector('.toggle')或el.classList.contains('error'),说明类名已被当作逻辑标识
怎么在不动 DOM 结构的前提下套上 BEM 类名
老项目 HTML 嵌套深、语义乱,硬拆结构会牵一发而动全身。务实做法是「语义映射」而非「结构重排」:
- 原代码:
<div class="form"><input></div>→ 改为:<div class="form-item"><input class="form-item__input"></div> - 对应 CSS 中删掉所有孤立的
.input、.error规则,只保留.form-item__input和.form-item__input--error - 禁止写
.form-item .input这类后代选择器——它绕过 BEM 隔离,且未来无法迁移到 CSS Modules 或 Shadow DOM - 若旧 JS 依赖
.form input,先加临时 wrapper:<div class="legacy-form">...</div>,CSS 里用.legacy-form .form-item__input过渡
Block 名怎么起才不埋雷
Block 名不是加前缀,而是定义归属边界。起错名字,三年后没人敢动它:
立即学习“前端免费学习笔记(深入)”;
-
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 业务类严格分离
为什么新写的 BEM CSS 还是被旧样式压住
不是 BEM 写错了,是旧 CSS 权重太高:ID 选择器、!important、深层嵌套(如 #header .nav li a:hover)让 .button__text 根本没机会生效。
- 先用 DevTools 的 Computed Styles 面板点开关键元素,看哪条规则“正在生效”——十有八九是旧选择器在赖着不走
- BEM CSS 文件必须在 HTML 中
<link>位置严格位于旧 CSS 之后(哪怕只是逻辑上后加载) - Webpack/Vite 项目可用
@layer分层:@layer legacy { /* 旧规则 */ }+@layer bem { /* 新规则 */ },现代浏览器按层覆盖 - 慎用
.legacy-wrapper { all: unset; }—— 它清空所有继承,可能让字体、颜色全丢,仅限强污染隔离场景
最难的不是写对 __ 和 --,而是每次加新 class 前停半秒:它属于谁?能不能独立存在?删掉这个 modifier,block 还能正常渲染吗?


















