优先重构被多页面复用、类名泛化、依赖!important或深层选择器、导致样式泄漏和JS硬编码的模块,通过添加wrapper class和BEM命名(如main-nav__item)实现安全迁移。

别从全局搜索替换 .btn 或 .nav 开始——这一步踩下去,90% 的线上样式崩坏和 JS 逻辑断裂就已注定。
哪些模块该优先重构,而不是碰全局类名
真正拖慢迭代的,是那些你改完一个地方、第二天收到三张样式 bug 工单的模块。它们有明确特征:
- 被至少两个以上页面或组件复用,但类名泛化(如
.list、.header) - 开发者频繁加
!important或靠深层后代选择器(如.page .content .item a)才能覆盖 - 上线后偶发「弹窗按钮变宽」「侧边栏对齐错乱」,最后发现是另一个模块的
.active泄露覆盖 - JS 里大量出现
document.querySelector('.toggle')或el.classList.contains('error'),说明类名已被当逻辑标识硬编码
这类模块就是迁移起点。先给它加 wrapper class,比如把旧 <div class="nav"></div> 改成 <nav class="main-nav"></nav>,后续所有新增子元素都走 main-nav__item、main-nav__toggle。
HTML 不动结构,怎么安全套上 BEM 类名
老项目 DOM 嵌套深、语义乱,硬拆结构会牵连事件绑定和表单校验。务实做法是「语义映射」而非「结构重排」:
立即学习“前端免费学习笔记(深入)”;
- 原代码:
<div class="form"><input></div>→ 改为:<div class="login-form"><input class="login-form__input"></div> - CSS 中立刻删掉所有孤立的
.input、.error规则,只保留.login-form__input和.login-form__input--error - 禁止写
.login-form .input这类后代选择器——它绕过 BEM 隔离,且未来无法迁移到 CSS Modules 或 Shadow DOM - 若旧 JS 依赖
.form input,先加临时 wrapper:<div class="legacy-form"></div>,CSS 里用.legacy-form .login-form__input过渡
BEM 类名写好了,但样式还是被旧 CSS 覆盖?
问题不在命名,而在层叠逻辑没理清:
-
<link>必须严格位于旧 CSS 之后(哪怕只是逻辑上后加载),否则.button__text权重再低也压不住#header .nav li a:hover - 用
@layer显式分层:@layer legacy { /* 旧规则 */ }+@layer bem { /* 新 BEM 规则 */ },现代浏览器按层覆盖 - 对旧 DOM 容器加命名空间根类,如
legacy-header,并在其 CSS 开头写.legacy-header { all: unset; }(慎用,仅限强污染场景) - 新 BEM 组件必须显式重置基础样式:
box-sizing: border-box、margin: 0、padding: 0,避免继承旧全局重置的副作用
JS 里硬编码的类名怎么平滑过渡
不能直接删 .btn,得让新旧共存一段时间:
- 执行命令:
grep -r "document\.querySelector\(['\"].*\.[a-z]\+['\"]\)" src/ --include="*.js",把结果列成迁移清单——这些类不能删,只能加别名过渡 - 旧 HTML 若已有
class="btn btn-red",先保留btn-red作别名,新代码只用button--primary,逐步替换 - 所有 Modifier 必须和 Block 共存于同一元素:
class="button button--primary"✔️,class="button--primary"单独用 ❌ - 禁止在 JS 中用
el.classList.toggle('error'),而应统一用el.classList.toggle('form-item__input--error'),否则状态封装就断了
最容易被忽略的是:BEM 不是重命名工具,而是用命名契约显性化组件边界。一旦开始迁移,Block 名就得真实对应 DOM 结构(如 product-card 而非 card),否则半年后你根本没法反向追溯哪个样式控制着购物车里的那个价格标签。


















