死代码难删的根本原因是类名归属不明确,BEM 通过显式命名规范(如 .card__header)界定组件边界,使工具能精准识别和扫描,但需严格遵循格式且须检查 JS 隐性依赖。

死代码难删,根本原因是类名没归属
传统 CSS 里一个 .btn 可能被散落在十几个组件、七八个页面中,还可能被 JS 动态拼接、第三方库覆盖、CSS Modules 导出消费。你 grep 到它“没在 HTML 里出现”,不代表它没被用——它可能藏在 el.className += ' btn-disabled' 这种字符串拼接里,也可能被 styles['btn--loading'] 显式引用。BEM 不解决“所有引用”,但它让「哪些类名属于哪个组件」这件事从隐式变成显式:只要组件下线,.card__header、.card--featured、.card__footer--compact 这整套类名就天然可被识别为一组,边界清晰。
工具能识别 BEM 类名,前提是结构合规
自动扫描工具(如 purgecss 或 postcss-bem-linter)不是靠猜,而是靠模式匹配。它们依赖 BEM 的固定分隔符来提取合法块名:
-
purgecss配extractors时,必须明确匹配__[a-z]和--[a-z]模式,否则.button--loading会被当成普通字符串漏掉 -
postcss-bem-linter要求你在配置里显式声明components: ['button', 'card', 'form-field'],否则它无法区分.icon是独立工具类,还是.button__icon的缩写 - 若写了
.btn-large或.modal-title这类非 BEM 格式,工具直接放弃识别——这类“伪类名”在 CI 的.rejected报告里占比超 70%
删之前必须确认三类 JS 层面的隐性依赖
BEM 类名看似只管样式,但实际常被 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 显式消费,不能只看模板里有没有用 - 留意第三方封装层:比如 Ant Design 的
Button内部用ant-btn-primary,你项目里写的.button--primary和它无关;但若写了.ant-btn__icon这种强行套 BEM 的类,既不生效,又污染统计结果
渐进式清理比“全量删除”更可靠
没人能一次清完所有死代码。重点是建立可持续的节奏:
立即学习“前端免费学习笔记(深入)”;
- CI 中加
npx purgecss --css src/**/*.css --content src/**/*.tsx --rejected --out .purge-report/,把.rejected文件归档,每周抽检前 10 个被标记为“未使用”的类名 - 发现大量
.btn-large类被误报?说明团队没严格执行 BEM,得先修复命名习惯,再推进清理 - 每次 PR 合并前,用
stylelint强制校验 BEM 结构,卡住card-title(缺双下划线)、button--primary--loading(修饰符嵌套)这类违规写法
真正难的不是“怎么删”,而是“删了会不会影响 JS 行为”。BEM 提供的明确归属关系,只是让这个问题变得可定位、可验证、可收敛——而不是靠人肉翻几十个文件去猜。


















