答案是先筛出三类高危类名再动手:被js硬编码的类(如document.queryselector('.btn'))、多人反复修改的混杂类(如common.css中的.sidebar)、语义重叠的泛化名(如.title/.header-title),并用grep确认上下文,html只加归属不拆结构,js兼容需过渡,css须分层加载。

先筛出三类高危类名再动手
盲目全局替换 .btn 或 .card 会立刻让 JS 绑定失效、样式错乱、埋点上报中断。真正该优先处理的是:被 document.querySelector('.btn') 硬编码的类;在 common.css 里反复被多人修改的混杂类(如 .sidebar);以及 HTML 中散落但语义重叠的泛化名(如同时存在 .title、.header-title、.modal-title)。用 grep -r "\.btn" src/ --include="*.html" --include="*.js" --include="*.css" 扫一遍,确认所有上下文,比直接改名重要十倍。
HTML不动,只加归属不拆结构
老项目里 <div class="form"><input></div> 这种结构,硬改成 form__input 会破坏表单校验逻辑和事件监听。正确做法是原地升级语义:
把 class="form" 改成 class="login-form";<input> 加上 class="login-form__input";
CSS 中删掉所有孤立的 .input 规则,只保留 .login-form__input 及其修饰符。
禁止写 .login-form .input 这类后代选择器——它默认你永远不重构父容器,而 BEM 的契约是“删掉 login-form,子元素仍能渲染”。
JS兼容必须过渡,不能一刀切
旧代码里 el.classList.contains('btn') 或 document.querySelector('.nav-item') 不能直接删,否则交互立刻挂掉。临时方案是:
改用 document.querySelector('[class*="btn"]') 替代 querySelector('.btn'),更宽松也更安全;
在 HTML 中保留旧类作注释:<button class="btn /* legacy */ button__base button--primary"></button>;
所有 data-class="btn"、className.includes('btn') 这类非标准用法,必须逐个定位并替换为新命名;
服务端模板渲染(如 EJS、Thymeleaf)里的类名引用,也要同步检查,否则上线后页面空白。
样式层要分层加载,别让权重打架
旧 CSS 大量使用 #header .nav li a:hover 这类高权重选择器,甚至带 !important,而新写的 .header__nav-link--hover 根本压不住它。解决方案只有两个:
确保 BEM CSS 的 <link> 标签严格放在旧 CSS 之后;
或用 @layer 显式分层:@layer legacy { /* 旧规则 */ } + @layer bem { /* 新BEM规则 */ }。
如果旧样式污染极强,可给遗留 DOM 加命名空间根类,如 legacy-header,并在其 CSS 开头加 .legacy-header { all: unset; }(慎用,仅限强污染场景)。
旧项目迁移最易被忽略的不是怎么写新类名,而是谁还在 JS 里读取旧类、服务端模板是否漏改、CSS 加载顺序有没有被构建工具悄悄打乱——这些地方一漏,BEM 再规范也白搭。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











