前端项目如何识别和清理Dead Code提升打包效率

UX-俊俊 优化 阅读 2,191
赞 10 收藏
二维码
手机扫码查看
反馈

Dead Code 的识别工具,别瞎手动找

很多人一听到死代码清理就开始手动翻代码,这是最蠢的做法。我之前接手一个老项目,接手人告诉我有大量死代码,让我手动清理。折腾了两天发现根本找不完,而且还不敢删,万一哪里引用了呢?后来我直接上了工具,效果立竿见影。

前端项目如何识别和清理Dead Code提升打包效率

我一般用两种工具:Webpack Bundle Analyzer 和 unimported。前者看打包结果,后者直接扫描整个项目。unimported 这个工具特别好用,一行命令就能扫出所有没被引用的文件:

npx unimported src/

扫出来的问题文件基本90%都是真的死代码,剩下10%可能是动态导入或者配置文件被误判。这里有个坑需要注意:如果用了动态路由 import,这些工具很容易误判。所以扫描结果出来后不能盲目删除,一定要人工确认一下。

我的写法,亲测靠谱

我处理死代码的流程是这样的:先用工具扫描,把可能的候选文件列出来,然后分批处理。比如我最近重构的一个电商项目的商品详情页,发现有好多以前的组件文件早就没人用了。

// 先用 unimported 扫描
// 找到这些文件可能有问题
// - components/OldProductCard.jsx (not imported)
// - utils/deprecated-helpers.js (not imported) 
// - styles/legacy.css (not imported)

// 然后我写个小脚本批量检查
const fs = require('fs');
const path = require('path');

function checkFileUsage(filePath, searchDir) {
    const content = fs.readFileSync(filePath, 'utf8');
    let found = false;
    
    // 递归搜索整个项目目录
    function searchInDir(dir) {
        const files = fs.readdirSync(dir);
        for (const file of files) {
            const fullPath = path.join(dir, file);
            if (fs.statSync(fullPath).isDirectory()) {
                searchInDir(fullPath);
            } else {
                const fileContent = fs.readFileSync(fullPath, 'utf8');
                if (fileContent.includes(path.basename(filePath))) {
                    console.log(Found reference in: ${fullPath});
                    found = true;
                }
            }
        }
    }
    
    searchInDir(searchDir);
    return found;
}

这种做法的好处是双重保险,工具扫描快速定位,手动检查避免误删。我一般先把疑似文件移动到专门的 dead-code-backup 目录,确认没问题后再彻底删除。这样即使删错了也能快速恢复。

这几种错误写法,别再踩坑了

最常见的错误就是看到没引用就删,我之前就踩过这个坑。有一次删除了一个看起来没人用的 CSS 文件,结果是某个第三方插件动态加载的样式,删了之后页面样式全乱了。现在我都会检查这个文件是否被动态引用:

  • 动态 import:import(${dynamicPath})
  • 配置文件里的路径引用
  • 某些库的约定式加载
  • 构建工具的特殊处理

还有一个坑是只看当前分支,不考虑其他分支的代码。我见过有人删除了测试环境的代码,但那些代码在测试分支里是必需的。所以一定要全局搜索,不只是当前分支。

还有一种情况是条件编译,比如下面这种代码:

// 以前的老代码,现在可能永远不执行
if (process.env.NODE_ENV === 'legacy-mode') {
    // 这些代码可能永远不会走
    import('./legacy-logic.js');
}

这种代码看起来是死代码,但如果你不确定有没有地方设置了 legacy-mode,千万别急着删。我一般会先全局搜索这个环境变量,确认没有人设置后再删除。

React/Vue 项目特别注意

前端框架项目有特殊的死代码问题。React 里最容易出现的是未使用的组件导出,Vue 里则是未注册的组件定义。我用 ESLint 配合一些插件来检测:

{
  "extends": [
    "@typescript-eslint/recommended",
    "plugin:react/recommended"
  ],
  "plugins": [
    "unused-imports",
    "react-hooks"
  ],
  "rules": {
    "no-unused-vars": "off",
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "warn",
      {
        "vars": "all",
        "varsIgnorePattern": "^_",
        "args": "after-used",
        "argsIgnorePattern": "^_"
      }
    ]
  }
}

但 ESList 也不是万能的,对于动态组件名还是检测不出来:

// 这种动态组件名 ESLint 检测不到
const ComponentName = 'OldComponent';
const Component = React.createElement(ComponentName);

所以框架相关的死代码还是要结合工具扫描来处理。

实际项目中的坑

大型项目最麻烦的是共享库的死代码判断。我负责的一个项目有十几个子应用共享一套组件库,某个组件在一个子应用里没用,不代表其他应用不用。这时候就需要跨项目关联分析,单纯的技术手段解决不了这个问题。

我当时的做法是建立一个依赖关系表,记录每个组件在哪些项目里被使用。每次删除前都要查表确认。虽然麻烦,但避免了很多问题。

还有国际化资源文件,很多翻译文本可能很久没更新了,但不代表没人用。我建议先用工具标记,然后让产品经理确认要不要删。毕竟有些功能可能下个版本才上线,现在删了后面又要重新加回来。

清理时机很重要

不要在发布前清理死代码,这是我的血泪教训。有一次我临发布前清理了代码,结果因为删除了一个被动态引用的配置文件,导致线上环境异常。现在我都在需求间隙期处理,给足够的时间验证。

另外建议写个简单的回归测试脚本,验证删除前后功能是否正常:

#!/bin/bash
# 删除前备份
cp -r src src-backup

# 执行构建
npm run build

# 运行基本测试
npm run test

# 如果有问题就恢复
if [ $? -ne 0 ]; then
    echo "Build failed, restoring backup..."
    rm -rf src
    mv src-backup src
fi

虽然自动化程度不高,但至少有个安全网。

最后一点建议

死代码清理是个持续性工作,不要指望一次性清理完。我现在的做法是在代码审查时顺便看看是否有明显废弃的代码,发现就立即处理。这样积少成多,项目就会越来越清爽。

以上是我个人对这个 Dead Code 清理的完整讲解,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论