coverage面板标出的“90%以上未使用”代码不等于首屏死代码,必须联动performance面板锁定fp至lcp间首屏执行窗口,再过滤业务bundle中连续50+行灰色、具业务语义的模块,并通过注释import+重录验证fcp/lcp无变化后方可安全删除。

直接用 Coverage 面板标出“90% 以上未使用”的代码,不等于它就是首屏死代码——关键在时间窗口和上下文。Coverage 默认统计整页生命周期,而首屏只关注从导航开始到 LCP 完成前那几十毫秒内实际执行/应用的部分。必须把 Coverage 和 Performance 联动起来,再过滤业务 bundle,才能精准揪出真正可删的冗余。
锁定首屏执行时间窗口(不是整页)
Coverage 面板本身不区分时机。某段 JS 可能在用户点击“展开详情”后才运行,它会被标为“已使用”,但对首屏毫无意义;反过来,一个 polyfill 因浏览器支持被跳过,又可能被误标“未使用”。所以第一步不是开 Coverage,而是先打开 Performance 面板:
- 勾选 Enable advanced paint instrumentation(确保能准确定位渲染节点)
- 点击录制 → 刷新页面 → 等主图可见、文字可读、核心按钮可点后停止
- 在火焰图中找到 FP → FCP → LCP 的连续区间,右键该时间段 → Save profile
- 回到 Coverage 面板,用这个 profile 重载分析,此时灰色行才代表“首屏时间内没执行”
只聚焦你自己的业务 JS bundle
Coverage 默认列出所有资源:node_modules、webpack runtime、polyfills、vendors chunk……这些不能删,也不该纳入“首屏死代码”判断范围:
- 手动折叠或忽略 node_modules/、core-js、regenerator-runtime、react-dom 等路径
- 只展开你项目产出的主 bundle(如 main.[hash].js 或 _app.[hash].js)
- 跳过覆盖率 >90% 的小工具模块(如 debounce.js),它们常被间接调用,Coverage 捕捉不到全链路
- 重点标记:连续 50+ 行灰色、文件名含业务语义(如 ProductFilterModal.js、LegacyCheckoutFlow.js)、且 import 后从未触发 render 或事件监听器
验证 CSS 冗余要绕过动态类名陷阱
Coverage 对 CSS 的判定基于原始样式表是否被浏览器解析并匹配 DOM,但现代前端大量使用动态类名:
- classNames、styled-components、Emotion 生成的运行时样式,Coverage 根本看不到
- @media 查询未命中当前视口宽度,会误报为“未使用”
- JS 动态插入的 rule(insertRule)或通过 classList.add('is-open') 添加的类,Coverage 不追踪
- 真要确认某条 CSS 是否可删:在 Elements 面板右键对应元素 → Break on > attribute modifications,再手动触发交互,看是否动态加 class
- 搜索项目中所有 classList.add、className +=、setAttribute('class',,把正则模式加入 purgecss safelist(如 /^is-.+$/)
安全删除前做两件事
别直接删代码。90% 灰色只是候选,不是判决书:
- 临时注释掉疑似模块的 import,比如 // import './LegacyAnalyticsTracker';,然后重新跑一遍 Performance 录制,确认 FCP/LCP 时间不变、无控制台报错、首屏 UI 完整
- 用 Lighthouse(Performance tab) 对比删除前后得分,重点关注 “Remove unused JavaScript” 和 “Reduce unused CSS” 项是否改善
- 如果是 Next.js 项目,同步检查 next.config.js 中 swcMinify 和 optimizeFonts 是否开启,并确认 sideEffects: false 在 package.json 中正确声明










