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中正确声明
