BEM能直接帮定位死代码,因其类名自带作用域(如.button__text仅属.button块),组件下线时相关类名可整体识别清理;配合postcss-bem-linter和purgecss配置语义提取器,并在CI中抽检.rejected文件,可高效识别伪类名与隐性依赖,实现渐进式清理。

为什么BEM能直接帮定位死代码
传统CSS里一个 .btn 类可能被散落在十几个组件、七八个页面中,删之前根本不敢确定它是否还在用。BEM把作用域写进名字本身:.button__text 只属于 .button 块,.card__footer 只属于 .card。这意味着:只要组件被下线或重构,对应的所有类名(.card__header、.card--featured)就天然可被整体识别和清理。
如何用工具自动扫描未使用的BEM类名
靠人眼 grep 不现实,尤其在大型项目里。关键不是“有没有工具”,而是工具能否理解 BEM 的语义层级:
- 必须配置
postcss-bem-linter的components列表,显式声明哪些是合法 Block(如['button', 'card', 'form-field']),否则它无法区分.icon是独立工具类还是.button__icon的缩写 - 搭配
purgecss时,需在content配置中启用extractors,专门匹配__[a-z]和--[a-z]模式,避免把.button--loading当成普通字符串漏掉 - CI 流程中加一道检查:运行
npx purgecss --css src/**/*.css --content src/**/*.tsx --rejected --out .purge-report/,把输出的.rejected文件归档,每周人工抽检前10个被标记为“未使用”的类名——你会发现其中超过 70% 是没按 BEM 规范写的“伪类名”,比如.btn-large或.modal-title
删之前必须确认的三个依赖关系
BEM 类名看似孤立,但实际存在隐性依赖。直接删可能让 JS 动态切换类名的逻辑失效:
- 检查 JS 中是否硬编码了类名:
el.classList.add('button--disabled')✅ 合法;el.className += ' disabled'❌ 危险——这个disabled不属于任何 Block,无法被工具识别,也容易误删其他地方的同名类 - 确认 CSS Modules 或 styled-components 是否导出了该类名:
import styles from './Button.module.scss'; console.log(styles['button--loading'])—— 如果有,说明这个修饰符被 JS 显式消费,不能只看 HTML 模板是否还用它 - 留意第三方库封装层:比如 Ant Design 的
Button组件内部用了ant-btn-primary,你项目里写的.button--primary和它无关,但若写了.ant-btn__icon这种强行套BEM的类,则既不生效又污染统计结果
渐进式清理比一次性删除更安全
没人能一次清完所有死代码,重点是建立可持续的清理节奏:
立即学习“前端免费学习笔记(深入)”;
- 新功能开发时,禁止新增非 BEM 类名;已有非 BEM 类(如
.clearfix)统一收口到src/styles/utils.css并加注释说明用途 - 每次组件重构(比如把
LegacyCard替换为Card),同步运行grep -r 'legacy-card' src/和purgecss,把匹配到的旧类名加入待删列表 - 最易忽略的是媒体查询里的修饰符:
.button--mobile若已由响应式设计接管,它在 PC 端永远不生效,但工具不会主动标记——这类必须靠设计系统文档反查状态定义表


















