人人都会AI编程

16.3 打包优化:资源排除、文件压缩、拆包策略

更新时间:2026-07-11

Electron 应用默认打包后体积通常不会太小——一个最简单的 Hello World 也会因为内嵌 Chromium 而占用 100MB 以上。随着项目依赖增加,体积更是可能快速膨胀到 300MB 甚至更大。这不仅影响用户下载体验,也会拖慢启动速度。好在 electron-builder 提供了丰富的配置项,配合一些工程化手段,你可以显著压缩最终的安装包大小。下面我们从三个最实用的方向展开:排除无用资源压缩可行文件、以及拆解包结构

16.3.1 资源排除:别把 node_modules 整个扔进去

很多 Electron 项目打包后体积巨大的首要原因,就是 node_modules 里的开发依赖、测试文件、文档、示例代码被原封不动地复制到了安装包中。electron-builder 默认会将 package.jsondependencies 列出的所有模块打包,但它不会自动帮你剔除这些模块中不需要的部分。

你可以通过配置 filesextraResources 来精确控制哪些文件需要放入包内。最有效的方式通常是 使用 glob 模式显式声明要包含的内容,而不是依赖默认行为。

示例:精确控制打包内容

# electron-builder.yml
files:
  - "dist/**/*"             # 前端构建产物
  - "main.js"               # 主进程入口
  - "preload.js"            # 预加载脚本
  - "node_modules/**/*"     # 先包含全部生产依赖...
  - "!node_modules/**/*.md"         # 再排除文档
  - "!node_modules/**/*.ts"         # 排除 TypeScript 源文件
  - "!node_modules/**/*.map"        # 排除 source map
  - "!node_modules/**/test/**"      # 排除测试目录
  - "!node_modules/**/docs/**"      # 排除文档目录
  - "!node_modules/.cache/**"       # 排除缓存

如果你使用了 TypeScript 编写主进程代码,需要提前编译到 dist/ 目录,那么在打包时只需包含编译后的产物,完全排除 src/ 源码目录。这在 electron-builder 的 files 中很容易实现,直接指定 "!src/**" 即可。

更激进的方案:手动审查依赖树

对于体积敏感的项目,推荐使用 npx depchecknpx npm-check 检查那些无意中被打包进来的庞大依赖。例如某个图片处理库可能连带引入了完整的 libvips 二进制文件,而你实际只用了其中一小部分功能。这种情况下可以考虑换用更轻量的包,或者使用 Webpack 等打包工具对主进程代码也进行 tree-shaking(electron-builder 不会自动做这些)。

16.3.2 文件压缩:UPX 和 ASAR 压缩

文件压缩可以分两个层面来操作:压缩 Electron 自身的二进制文件压缩应用业务代码

压缩 Electron 二进制

electron-builder 支持通过 compression 选项指定对最终安装包使用的压缩算法。例如在 Windows 上可以设置 NSIS 安装包的压缩级别:

nsis:
  compression: "7z"   # 使用 7-Zip 压缩,比默认的 lzma 更好

对于可执行文件的直接压缩,可以用 UPX(Ultimate Packer for eXecutables) 工具对 Electron 的二进制进行再压缩。electron-builder 内置了简单的 UPX 支持(仅限 Windows 和 Linux),通过在配置中启用即可,但需要 UPX 环境可用。不过需要注意:UPX 压缩后的可执行文件在首次启动时需要解压缩,可能导致少许启动延迟,而且有可能触发某些杀软的误报。因此在面向普通用户的正式发布版中要谨慎评估。

示例:启用 UPX 压缩

win:
  executableCompression: "upx"
# 或指定自定义 UPX 路径
upx:
  path: "/usr/bin/upx"

ASAR 压缩

Electron 默认使用 ASAR 格式将应用文件打包成一个单一归档,这本身就减少了一些小文件的存储开销。electron-builder 还可以对 ASAR 包进行进一步压缩:

asar:
  compression: "maximum"   # 启用最大压缩(内部使用 brotli)

设置后,业务代码和 Node.js 依赖会被高度压缩,读盘更高效,安装包体积也会减小。不过,ASAR 内的文件读取需要解压缩,对 CPU 有轻微消耗。通常这完全在可接受范围内,建议正式发布时开启 maximum

16.3.3 拆包策略:分离主进程、依赖与动态加载

拆包的核心目的是 减少每次更新需下载的数据量,并优化应用启动性能。如果全部文件打包成一个整包,哪怕你只修改了一行 UI 代码,用户也得重新下载整个 150MB 的安装包。通过合理的拆包可以让更新包缩小到几十 KB 甚至几 KB。

拆出依赖到独立 ASAR

electron-builder 允许你将指定的 node_modules 依赖分离到额外的 ASAR 文件中,这样主应用的 ASAR 只包含你自己的业务代码,体积很小。依赖被拆分后,更新时如果依赖没有变化,就可以只下载变化的业务包。

asar: true
extraResources:
  - from: "node_modules/some-heavy-lib"
    to: "extra_libs/some-heavy-lib"
    filter:
      - "**/*"

在应用中动态加载这些外置依赖时,需要调整 require 路径,或者使用 require.resolve 配合 asar 的虚拟文件系统来访问。虽然稍微增加了代码复杂度,但对大应用来说非常值得。

拆分渲染进程代码(webpack/code splitting)

如果你的渲染进程是用 Webpack 或 Vite 构建的大型单页应用,那么首先就应该启用代码分割(code splitting),按照路由拆分不同的 chunk。这不仅能加快首屏加载,也能让更新包更小。如果使用了 electron-updater,只需更新 asar 包即可,那些没变化的 chunk 文件 hash 不变,增量更新只会下载修改过的文件。

示例:Vite 构建配置

// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return 'vendor';  // 将第三方库合并为 vendor chunk
          }
        }
      }
    }
  }
}

拆出主进程与原生模块

更彻底的拆包做法是将主进程业务代码与那些体积大、不常更新的原生模块(如 sqlite3sharp 等)分别存放。这些原生模块可以放在额外的目录中,通过 extraResources 引入,主进程启动时自动加载。更新时,主进程的 asar 和这些原生模块可以独立下载、替换。不过这一方案需要手动管理模块的路径和版本兼容性,只推荐在体积优化极端重要的场景下使用。

16.3.4 实战检查清单

当你完成上述优化后,建议用以下方法验证实际效果:

  • 查看最终包解压后的目录结构。在 macOS 上可以右键 .app 选择“显示包内容”,找到 Contents/Resources/app(或 asar 包),检查是否引入了预期之外的大文件。
  • *使用 du -sh 或磁盘分析工具** 分析 app.asar 内各个文件夹的大小,找出最大的占空间模块,针对性修剪。
  • 对比增量更新包体积。通过 electron-updater 的日志查看实际下载的 blockmap 文件大小,确认拆包策略是否让更新包明显缩小。

真实案例:某款基于 Electron 的数据工具应用,初始打包后安装包高达 280MB。通过排除每个 node_modules 中的 markdown、test、example 目录,并启用 UPX 压缩 Electron 二进制,再将前端 chunk 拆细,最终安装包降到 145MB,增量更新包从原来的 50MB 降至 2~5MB。这一系列优化没有改动任何业务代码,纯靠打包配置完成。

打包优化不是一锤子买卖,而是持续迭代的过程。建议在项目初期就搭建好体积监控(比如用 CI 脚本比对每次构建的包大小),一旦发现某个依赖或资源导致包体积突变,马上定位并处理。这样你的 Electron 应用才能在保证功能完整的前提下,始终维持清爽、高效的发布形态。