Electron 应用默认打包后体积通常不会太小——一个最简单的 Hello World 也会因为内嵌 Chromium 而占用 100MB 以上。随着项目依赖增加,体积更是可能快速膨胀到 300MB 甚至更大。这不仅影响用户下载体验,也会拖慢启动速度。好在 electron-builder 提供了丰富的配置项,配合一些工程化手段,你可以显著压缩最终的安装包大小。下面我们从三个最实用的方向展开:排除无用资源、压缩可行文件、以及拆解包结构。
16.3.1 资源排除:别把 node_modules 整个扔进去
很多 Electron 项目打包后体积巨大的首要原因,就是 node_modules 里的开发依赖、测试文件、文档、示例代码被原封不动地复制到了安装包中。electron-builder 默认会将 package.json 中 dependencies 列出的所有模块打包,但它不会自动帮你剔除这些模块中不需要的部分。
你可以通过配置 files 或 extraResources 来精确控制哪些文件需要放入包内。最有效的方式通常是 使用 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 depcheck 或 npx 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
}
}
}
}
}
}
拆出主进程与原生模块
更彻底的拆包做法是将主进程业务代码与那些体积大、不常更新的原生模块(如 sqlite3、sharp 等)分别存放。这些原生模块可以放在额外的目录中,通过 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 应用才能在保证功能完整的前提下,始终维持清爽、高效的发布形态。