人人都会AI编程

22.2 依赖精简:无用依赖剔除、依赖替换优化

更新时间:2026-07-11

Electron 应用的安装包体积很大一部分来自 node_modules。一个典型的项目可能有几百甚至上千个依赖,其中不少并未在运行时真正使用,或者可以用更轻量的方案替代。这一节会从“剔除无用依赖”和“替换体积过大的依赖”两个维度,给出一些能在实际项目中立刻落地的优化手段。

22.2.1 无用依赖的精准剔除

Node.js 项目很容易积累“僵尸依赖”:装了但没用到,或者曾经用过但代码已删除却仍保留在 package.json 中的包。这些包不仅增加安装体积,还可能引入安全隐患。

1. 借助工具自动检测未使用的依赖

推荐使用 depcheck 快速扫描项目:

npx depcheck

它会分析你的代码文件,找出哪些 dependencies 中的包没有被 requireimport,以及哪些 devDependencies 没有在脚本或配置中被引用。注意 depcheck 不能 100% 准确识别像 electron-builder 这类在配置文件中静态引用的包,所以人工复核仍是必要的。

2. 区分 dependencies 与 devDependencies

在 Electron 项目中,只有主进程和预加载脚本中真正使用的包需要放入 dependencies;渲染进程的依赖如果经过打包工具(Webpack、Vite)处理,理论上所有依赖都可以放在 devDependencies,因为它们会被打包进输出文件中,不会以独立模块形式出现在最终的 app 里。

很多团队为了方便,会把所有东西都扔进 dependencies,这会导致最终安装包中保留大量只用于构建阶段的工具(如 babel 插件、测试框架)。合理划分能直接减少打包进 app.asar 的模块数量。

3. 清理 npm 的重复安装

运行 npm dedupe (或 yarn dedupe) 可以尝试将依赖树扁平化,减少重复安装相同包的不同版本。如果一个项目中同时依赖了 lodash@4.17.21lodash@4.17.20,两个版本都会被安装,这无疑是浪费。dedupe 通过提升依赖来合并它们。

4. 审视安装时被拉入的间接依赖

有些直接依赖本身很轻量,却会引入大量庞大的子依赖。可以使用 npm lsnpx cost-of-modules 查看每个包占用的磁盘大小,识别出体积异常大的包。例如,某些云服务 SDK 可能把签名、HTTP 客户端、XML 解析器等全部打包进来,但在桌面应用中我们可能只需要其中一个核心函数。这种情况下就值得考虑替换。

22.2.2 依赖替换优化:用小包代替大包

很多功能都有更轻量级的实现,有意识地替换依赖库可以显著降低依赖树体积。

1. Lodash → 原生方法或按需引入

Lodash 在早期极大提升了 JS 开发的便捷性,但现在大部分方法已有原生替代。比如:

  • _.get → 可选链 ?.
  • _.map / _.filter → 原生数组方法
  • _.debounce → 可从 lodash/debounce 单独引入,或使用 just-debounce-it 这类仅 200 字节的小包

如果项目只用到 Lodash 的个别函数,务必使用子路径导入(import debounce from 'lodash/debounce')而非整体引入,以避免打包时包含整个库。更彻底的做法是使用 lodash-es 配合 Tree Shaking,或者直接用部分功能的 micro-lib 替代。

2. Moment.js → Day.js / date-fns

Moment.js 体积庞大(压缩后 > 70KB),且已进入维护状态。Day.js 拥有与 Moment.js 几乎相同的 API,但体积仅 2KB,还支持插件按需加载。一般替换步骤:

npm remove moment
npm install dayjs

代码改动通常只需要全局替换 momentdayjs,并根据需要引入对应的插件(如 durationrelativeTime)。对于更复杂的时间处理场景,date-fns 的按需导入也非常灵活,体积控制得很好。

3. jQuery → 原生 DOM 操作或前端框架

在 Electron 的渲染进程中(通常已使用 Vue/React 等框架),jQuery 完全是多余的。如果仍然遗留了少量 jQuery 用法,建议用原生 querySelectorclassList 或框架的数据驱动方式取代。实在需要快速操作 DOM 又可以选用 zepto,但最推荐的方式还是消除这一层冗余。

4. AWS SDK / 云服务全量 SDK → 独立客户端或 REST 调用

如果你的应用只用到某个云服务的单一功能(例如上传文件到 S3),引入完整 AWS SDK 会显著增大安装包。现在 AWS 提供了模块化的 SDK v3,可以只安装所需的 client:

npm install @aws-sdk/client-s3

相比 aws-sdk 整体包,体积可以缩小一个数量级。类似情况也适用于阿里云、腾讯云等 SDK。更激进的方案是直接封装对应的 REST API 调用,仅引入 axiosnode-fetch(或直接使用 Node 18 内置的 fetch)。

5. 图片处理:用 sharp 替代 imagemagick/spawn 方案

sharp 是一个高性能的 Node.js 图片处理库,基于 libvips,安装体积比 spawn 一个 ImageMagick 进程的方案小得多,而且速度更快。如果应用中需要裁剪、缩放、格式转换等功能,选择 sharp 可以同时提升性能和减小依赖体积。

6. 大量工具函数 → 自研或内联

npm 中有许多“微小”的工具包,每个可能只有几十行代码。例如判断一个值是不是数字的 is-number、获取数组最后一元素的 arr-last 等。单独看体积不大,但安装时它们会带来各自的依赖树,以及 npm 权限文件、文档等负担。团队可以考虑把这些小函数统一维护成一个内部 utils.jshelpers.js 文件,既减少了依赖数量,也更利于代码评审和定制。

22.2.3 在构建阶段修剪依赖

除了源代码层面的剔除和替换,还可以在打包阶段进一步过滤掉不需要的文件:

  • electron-builder 的 files 配置:明确指定需要打包到 asar 中的文件或目录,排除 .md、测试文件、源代码 map 等。
  • 使用 .npmignorepackage.jsonfiles 字段:限制发布到 npm 的文件,间接影响依赖自身被拖入的杂物。
  • 利用 webpack 的 nodeExternals 精确控制:在主进程打包时(如果使用了 webpack 打包主进程),通过配置 externals 将某些原生模块或大型依赖排除在 asar 之外,按需使用 native-ext-loader 等方式处理。不过这种方式需要小心原生模块的打包路径问题。

22.2.4 真实案例:依赖精简带来的体积变化

以一个实际的企业级即时通讯应用(类似 Slack 的客户端)为例,在未优化前 node_modules 在安装包中占用约 45MB。经过以下步骤:

  1. 使用 depcheck 清理掉 8 个未使用依赖。
  2. moment 替换为 dayjs
  3. 将云服务 SDK 从全量包替换为模块化客户端。
  4. 移除所有 jQuery 引用。
  5. 合并十几个微型工具函数到项目的 utils 模块。
  6. 修正 dependenciesdevDependencies 的归属。

优化后 node_modules 大小降到约 22MB,安装包整体从 190MB 减至 165MB。对于需要频繁自动更新的桌面应用来说,这 25MB 的减少直接意味着用户下载更新时节省了流量和时间。

依赖精简不是一次性工程,应当作为代码审查和 CI 的一部分持续关注。每次引入新的外部包时,问自己一句:“这个功能我是不是可以用已有的依赖实现,或者用几行代码自己写?”。这种思考习惯带来的长期收益,要远大于一次性的暴力清理。