
简介这是一套面向Electron桌面应用开发者的打包依赖环境全家桶专门解决新下载的Electron项目在构建安装包时因依赖不完整而频繁报错的问题。压缩包内集齐三件核心组件electron-v8.5.5-win32-x64提供应用主程序运行环境nsis-3.0.4.2负责生成Windows安装引导程序winCodeSign-2.6.0则用于代码签名与证书校验三者均为Windows下的兼容版本可整体解压后复制到C:\Users\用户名\AppData\Local\electron\Cache目录下直接调用无需手动逐一下载匹配版本。资源整体为7z压缩格式体积约356.44MB内含三个组件对应的完整文件集合解压后即可按目录结构使用。对于刚接触Electron打包的初级开发者或需要快速搭建打包环境的工程师这套文件能显著降低环境配置难度避免因缺失签名工具或NSIS模板导致的反复失败。目前已有1548人学习下载适合作为本地打包环境的备件库。1. 周五下午你卡在三个绿色文件夹上Electron 打包全家桶到底在装什么周五下午急着发一版 exe 出去electron-builder 在日志里安静地写“Downloading electron-v8.5.5-win32-x64.zip”进度条绕了十分钟你才在缓存目录里看到三个带版本号的文件夹electron-v8.5.5-win32-x64、nsis-3.0.4.2、winCodeSign-2.6.0打包才真正跑起来。这就是常说的 Electron 打包全家桶Electron 本体负责运行时NSIS 负责把应用卷成安装程序winCodeSign 藏着一组改图标、写版本号、打数字签名的工具。很多用 electron 技术栈的人都被这三个包折磨过——不知道它们从哪来、缺了会怎样、版本为什么不能乱动。这篇就拆开讲清楚版本关系、最小配置和实际踩过的坑适合正在用 electron-builder 在 Windows 上出安装包、被体积和签名问题反复折腾的开发者。2. 版本对应关系与离线依赖先把镜像、缓存和三个包的依赖链捋顺2.1 为什么是这几个版本electron-builder 的版本锁定逻辑把这三个版本号当成“黑匣子”是最常见的状态。它们其实不是你自己指定的而是 electron-builder 在打包时按内部锁定的版本去下载的electron-v8.5.5-win32-x64 对应你项目里装的 Electron 运行时nsis-3.0.4.2 是 electron-builder 定制的 NSIS 引擎winCodeSign-2.6.0 则是一套 Windows 签名工具集。我一般会先查清一件事electron-builder 的版本和 NSIS、winCodeSign 资源包是一一对应死的升级 electron-builder 可能导致缓存里多出多个不同版本的 nsis 和 winCodeSign新旧混用时会出一些非常奇怪的“玄学报错”。这个锁定逻辑带来的直接结果就是谁先装谁后装并不重要打包时 electron-builder 会去远程仓库拉一个“带版本号前缀的压缩包”解压后放进缓存目录等待调用。如果你团队的构建机上已经有人跑过一次打包第二次打包根本不需要重新下载。但换一台新电脑或者升级了 electron-builder 版本这部分下载时间就会原封不动地还给你。为了不让团队成员各自去等一次网络下载我在团队内部约定缓存目录作为构建产物的一部分进行备份新机器先恢复缓存再打包。以下是 Windows 上 electron-builder 的默认缓存路径不同系统不一样平台缓存目录Windows%LOCALAPPDATA%\electron-builder\CachemacOS~/Library/Caches/electron-builderLinux~/.cache/electron-builder缓存目录里会按依赖名分文件夹electron 压缩包放在 electron 目录nsis 工具放在 nsis 目录winCodeSign 放在 winCodeSign 目录。你在打包日志里看到 “Downloading” 字样就是在补齐缓存看到 “Packaging” 字样说明依赖已经齐了。判断标准就这么简单。2.2 三个包在打包中分别扮演什么角色Electron 本体压缩包解决的是“运行时”问题。它内置了 Chromium 和 Node.js你的 JavaScript 代码在打包后会被塞进一个 asar 归档由这个本体加载执行。之所以单独强调 win32-x64是因为 electron-builder 对平台架构比较敏感——同一台机器如果要同时出 32 位和 64 位安装包会分别下载两个不同架构的 Electron 压缩包体积直接翻倍。NSIS 3.0.4.2 是安装程序引擎。electron-builder 没有自己重新发明安装包格式而是基于 NSIS 做了一个定制版它把应用文件压成一个 7z 归档再把 NSIS 的 stub 和这个归档拼起来生成你看到的 Setup.exe。整个安装过程就是 NSIS 在解压、写注册表、创建快捷方式、注册卸载项。所以 NSIS 的版本直接影响安装包的界面行为比如是否允许选安装路径、是否显示安装进度条、是否需要管理员权限。winCodeSign-2.6.0 里面是一组可执行工具最常见的是 signtool.exe 和 rcedit.exe。rcedit 负责改 exe 的图标、版本号、文件描述等资源段signtool 负责给 exe 打 Authenticode 签名。你配置了 certificateFile 和证书密码时winCodeSign 就会被调用没配置证书时它也负责把默认图标和版本信息写进 exe。很多人以为代码签名是“证书的事”其实签名动作本身完全依赖这个工具包。2.3 加速依赖下载的两种做法以及缓存目录的离线用途网络环境直接影响打包体验尤其是 Electron 压缩包动不动就上百 MB。我常用的做法是设置镜像源环境变量set ELECTRON_MIRRORhttps://npmmirror.com/mirrors/electron/ set ELECTRON_BUILDER_BINARIES_MIRRORhttps://npmmirror.com/mirrors/electron-builder-binaries/第一行让 electron 本体从国内镜像下载第二行让 nsis、winCodeSign 从 electron-builder-binaries 镜像下载。两个变量是独立的只配其中一个的话另一个仍然走官方源所以建议同时设置。镜像源的选择不用太纠结只要确认路径能直接拼出“electron-v8.5.5-win32-x64.zip”和“nsis-3.0.4.2.7z”这样的文件结构就行。离线构建则是另一个思路在能正常打包的机器上把 %LOCALAPPDATA%\electron-builder\Cache 整个目录拷贝到新机器对应位置再打包时会直接命中缓存日志里完全看不见 Downloading 字样。我帮同事处理过多次“打包卡在下载”的问题最后几乎都是归还缓存放行的。注意一点缓存目录的归属是当前用户级的如果 CI 或新机器用了不同账号路径要相应调整。3. 最小打包配置跑通用 electron-builder 在 Windows 上产出第一个 exe3.1 初始化工程并安装核心依赖electron 版本怎么锁先从一个干净的目录开始。这里假设项目名就叫 electron-pack-demoNode.js 版本不低于 14因为 electron-builder 对老版本 Node 支持有限。初始化并安装依赖mkdir electron-pack-demo cd electron-pack-demo npm init -y npm install --save-dev electron8.5.5 electron-builder把 electron 固定在 8.5.5 是因为团队项目的运行时版本要和缓存的 electron-v8.5.5-win32-x64 对齐。electron-builder 会自动探测当前项目里 electron 的版本然后拼出下载文件名所以如果你装的是 electron 9.x缓存里就会出现另一个版本号的压缩包。同一个项目里不要混用否则每次打包可能都在下载新东西。安装完成后package.json 里至少要有 main 字段指向应用的入口文件。最简单的方式是新建一个 main.jsconst { app, BrowserWindow } require(electron); app.whenReady().then(() { const win new BrowserWindow({ width: 1024, height: 768, webPreferences: { contextIsolation: true } }); win.loadFile(index.html); });这段代码的逻辑是等 Electron 应用就绪后创建一个 1024x768 的主窗口并加载本地 index.html。窗口参数里的 contextIsolation 保持默认开启避免后续升级 Electron 时出现渲染进程沙箱警告。index.html 里随便写一个标题打包时它会被一起收进 asar。3.2 electron-builder 的配置项解析win、nsis、files 三段配置electron-builder 有两种配置方式写在 package.json 的 build 字段里或者单独建 electron-builder.yml。我习惯用 package.json因为少一个文件而且读取优先级不容易搞混。下面是一个最小可用的配置文件{ name: electron-pack-demo, version: 1.0.0, description: electron packaging demo, main: main.js, scripts: { pack: electron-builder --win }, build: { appId: com.example.electronpackdemo, productName: ElectronPackDemo, files: [ main.js, index.html, package.json ], win: { target: [ { target: nsis, arch: [x64] } ], icon: build/icon.ico }, nsis: { oneClick: false, allowToChangeInstallationDirectory: true, createDesktopShortcut: true, shortcutName: ElectronPackDemo } } }配置分为三层files 决定哪些文件进 asarwin 决定目标平台和架构nsis 决定安装包行为。files 里只列入口文件和页面资源node_modules 不需要手写electron-builder 会自动分析 main.js 里 require 的模块并打包进去。win 里把 target 精确到 nsinstal 和 x64出包结果就只会有 64 位的 NSIS 安装包。icon 字段的路径是相对项目根目录的要求是 .ico 格式至少包含 256x256 尺寸。如果没有准备图标第一次跑通可以暂时删掉这一行electron-builder 会用默认 Electron 图标但后续发布前必须换掉否则产品名旁边永远是个外星人头像。3.3 第一次打包与产物目录验证在项目根目录执行打包命令npm run pack由于刚才配置了国内镜像源这一步不会等待太久但第一次仍会下载 nsis-3.0.4.2 和 winCodeSign-2.6.0。日志里依次出现 “downloading”、“extracting” 和 “building” 三个阶段。构建完成后项目根目录会出现一个 dist 文件夹里面有两个文件尾缀带 “Setup” 的是安装程序尾缀带 “Blockmap” 的是增量更新映射文件。我建议拿到产物后先手动安装一遍而不是直接丢给测试。安装过程中重点观察刚才配置的 allowToChangeInstallationDirectory 是否生效——如果你看到安装界面底部没有“选择目录”按钮说明 nsis 配置没被正确读取。常见原因是 package.json 里 build 字段写在了 scripts 之前位置不影响解析但有些编辑器会把 JSON 格式校验标红导致你以为配置无效实际问题出在引号或逗号上。跑完第一次打包后去看缓存目录如果 nsis 和 winCodeSign 文件夹已经躺在那了说明全家桶下载完毕下次打包可以直接断网测试。4. NSIS 安装包定制卸载逻辑、安装路径与权限的坑4.1 nsis-3.0.4.2 在 electron-builder 里怎么被调用上一章我们完成了最小打包这一章开始定制 NSIS 安装行为。先明确一件事NSIS 不是 electron-builder 写的脚本语言而是一整套安装程序制作框架。electron-builder 做的是把 NSIS 的 makensis 编译器、预置安装脚本模板和你配置的 nsis 参数拼在一起生成一个带 UI 的 Setup.exe。你平时看到的“下一步”“安装”“完成”按钮全部来自 NSIS 的现代 UI 组件。nsis-3.0.4.2 这个版本号其实很少有人单独下载它因为 electron-builder 会在打包时自动去 electron-builder-binaries 镜像拉取一个压缩包解压后临时调用。它内部包含两个关键文件makensis.exe 是编译器Stubs/ 目录里是各种安装程序外壳。你在配置里写 oneClick、allowToChangeInstallationDirectory最后都会被翻译成传给 makensis 的指令参数。我在定制安装包时坚持一个原则优先改 electron-builder 的 nsis 配置项不要轻易改 NSIS 脚本模板。因为 electron-builder 每次升级都可能更换模板文件你手改的脚本在下次升级后会失效或被覆盖。除非是改安装界面的启动画面图片或修改安装完成后是否自动运行这种偏门需求否则配置项足够用。4.2 定制安装程序安装路径、桌面图标与卸载行为最常用的定制需求有三个安装目录可手选、桌面快捷方式开关、卸载时是否清除用户数据。下面是一组我在实际项目里会用到的配置nsis: { oneClick: false, perMachine: false, allowElevation: true, allowToChangeInstallationDirectory: true, installerIcon: build/installer.ico, uninstallerIcon: build/uninstaller.ico, installerHeaderIcon: build/header.ico, createDesktopShortcut: true, createStartMenuShortcut: true, shortcutName: ElectronPackDemo, deleteAppDataOnUninstall: false, runAfterFinish: true }逐个说明oneClick 改为 false 会启用“辅助安装向导”模式安装器先展示欢迎页和目录选择页而不是一键装完。perMachine 设为 false 表示按用户级安装不需要管理员权限装到当前用户的 AppData 目录如果设为 true安装包会请求管理员权限并安装到 Program Files。allowElevation 控制的是安装器是否允许动态请求 UAC 提权。perMachine 为 false 时这个选项一般跟着 false 走避免安装器一半权限一半没权限的割裂状态。deleteAppDataOnUninstall 是很多人会忽略的坑如果设为 true用户在卸载时会连本地数据库和登录状态一起删掉对于工具类应用我建议设为 false让用户数据保留至少在卸载时给一个提示选项。runAfterFinish 控制安装完成后是否立即启动应用这个看产品习惯不设默认是 true。安装路径的选择逻辑也值得注意当 perMachine 为 false 时electron-builder 会把默认安装路径设置到当前用户的 AppData\Local\ProgramsproductName 目录下。这种做法的好处是即使用户不是管理员也能安装坏处是某些安全类软件会对用户目录下自启动的可执行文件特别敏感。我自己做过对比同样是未签名安装包装到 Program Files 的触发拦截概率比装到用户目录低一些这是实测体会不一定算规则。4.3 安装包体积优化与安装速度的权衡安装包体积是逃不过的话题。electron-v8.5.5-win32-x64 压缩包解压后接近 200MBNSIS 引擎会把整个应用目录压成一个 7z 归档。压缩级别越高Setup.exe 越小但安装时解压越慢。electron-builder 提供了一组压缩参数nsis: { compression: lzma, differentialPackage: true, unpackDirName: app-unpacked }compression 支持 zip、lzma、bzip2 三种。默认是 lzma压缩率最高但打包时间也最长。如果你的应用里有很多二进制资源比如原生模块或 ffmpeg 库zlib 会更稳因为 lzma 在个别杀毒软件的实时扫描下有可能被误判。differentialPackage 设为 true 会生成一个体积很小的增量补丁包方便后续版本升级时只下载差异部分。除了压缩算法体积优化要靠排除文件。files 配置里有一个规律我觉得值得记住被 asar 收录的文件越多安装包越接近全量复制而 electron-builder 默认会把 app-builder 依赖、测试脚本、.md 文档都打进去。我通常会在 files 里明确排除不必要的内容files: [ main.js, index.html, package.json, !assets/screenshots/**, !test/**, !**/*.map ]map 文件对线上应用没有任何用处却会让体积多出几十 MB这一条建议直接加。screenshots 这类静态资源如果不被运行时读取也建议排除。每做一次排除操作对比一下 dist 目录下 Setup.exe 的体积变化慢慢就能对“哪些文件值得进安装包”形成肌肉记忆。5. winCodeSign-2.6.0 代码签名避坑为什么签完名还是被拦截5.1 签名在打包流程里的位置与必要性winCodeSign 在整个打包流程里承担两件事改 exe 资源和打 Authenticode 签名。它的位置在 NSIS 制作安装包之前——electron-builder 先调用 rcedit 修改应用的 exe 图标和版本信息接着调用 signtool 给 exe 签名最后才把签名后的 exe 传给 NSIS 封装成安装程序。所以如果你在打包日志里看到 winCodeSign 目录被读取但随后出现 “skipped signing”说明签名步骤被跳过了。签名不是可有可无的东西。Windows 10 以上系统对来自网络的可执行文件有“Mark of the Web”检测未签名或签名无法验证的 exe 会触发 SmartScreen 的蓝色警告框上面写着“Windows 已保护你的电脑”。真实的产品分发走这条路是必选项不是加分项。我有一次给内部工具打包懒得配证书结果同事安装时 Windows Defender 直接提示风险最后还不是得回去补签名配置重打。winCodeSign-2.6.0 里包含的工具可以独立于 electron-builder 使用。遇到签名问题需要排查时我会直接去看缓存目录里的 winCodeSign 文件夹找到 signtool.exe 手动执行验证命令比反复打包试错快得多。5.2 自签名证书练手但别指望它过 SmartScreen正式的代码签名证书需要去 CA 机构购买一年花费不小。在正式证书到手之前可以用自签名证书先把签名链路完整跑通。Windows 上生成自签名证书用 PowerShell 一条命令New-SelfSignedCertificate -Type CodeSigningCert -Subject CNElectronPackDemo Test -CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)执行后会生成一张有效期为 1 年的代码签名证书并自动导入当前用户的证书库。要把它导出成 electron-builder 能读的 pfx 格式需要再用两条命令$cert Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -like *ElectronPackDemo* } $password ConvertTo-SecureString your-password -AsPlainText -Force Export-PfxCertificate -Cert $cert -FilePath build/cert.pfx -Password $password然后在 build.win 配置里引用win: { certificateFile: build/cert.pfx, certificatePassword: your-password }自签名证书能让打包日志里出现 “signed with sha256” 字样安装的时候也能看到发布者名字但用户机器的证书信任链里没有你的自签名根证书SmartScreen 照样弹警告。这条链路最大的价值在于验证 electron-builder 读证书、算哈希、打签名的流程没毛病正式证书拿到手后替换 pfx 文件就行。5.3 三个坑证书找不到、时间戳失败、双签名缺失坑 1打包日志显示 “skipped signing”安装包发布者为“未知”。现象打包流程正常结束产物也能跑但右键查看 exe 属性数字签名标签页空白或显示“该文件的数字签名无效”。原因certificateFile 配置没生效。最常见的是路径写错——electron-builder 解析相对路径时基于项目根目录如果你把 pfx 放在 build 目录下却写成了 build/build/cert.pfx 就会找不到。还有个隐蔽情况electron-builder 读取的是 win 块下的 certificateFile不是根级配置。解决把证书路径改成绝对路径测试一遍确认能读到后再改回相对路径。如果使用的是环境变量传入证书密码检查 CI 或当前 shell 里环境变量是否真的存在。坑 2签了名但 SmartScreen 仍显示“未知发布者”。现象安装包已经带签名属性里能看到发布者名字但弹窗还是全蓝色警告。原因签名有效不代表信任链建立。SmartScreen 看的是“证书是否来自受信任的根证书颁发机构”以及“证书印章是否被足够多的用户接受过”。自签名证书和刚买的 OV 证书都会遇到这个问题OV 证书的情况是部分用户点击“更多信息”可以继续但信任建立需要时间。解决更换为 EV 代码签名证书它会把签名印章做到本地可信根里SmartScreen 警告能直接消掉。如果暂时没有 EV 证书OV 证书配合稳定的时间戳服务器也能减少一部分拦截但不要指望 100% 消除。坑 3签名时报 “The timestamp service is not available” 或安装包在无网环境安装时签名校验失败。现象打包日志里出现时间戳服务器连接失败或者安装包在联网环境正常、到客户内网就提示签名损坏。原因electron-builder 签名时会向时间戳服务器发请求把签名时刻记录到签名数据里保证证书过期后签名仍然有效。客户内网断外网时Windows 无法验证时间戳。解决在 win 配置里显式指定时间戳服务器win: { certificateFile: build/cert.pfx, certificatePassword: your-password, signtoolOptions: { signAndTimestamp: true } }或者更直接的做法是手动重新签名时加上/tr参数指定时间戳地址。实际上electron-builder 默认会对 exe 做时间戳但构建机网络不好时可能静默跳过。我在一次离线构建中发现签名完整但时间戳缺失后来固定了时间戳服务器地址这类问题基本绝迹。以上三条是签名环节最容易遇到的。记住一个原则签名日志里的关键字是 evidence看到 “Signed” 和 “Timestamp” 两个输出同时出现才算真正完成缺一个都算翻车。6. 让 Electron 打包成为可复用的流水线缓存复用与签名自检打包这件事一旦变得依赖网络下载和本地缓存就天然不适合手工反复操作。我最后的习惯是给项目加一个验证脚本打包完成后自动检查安装包的签名状态Get-AuthenticodeSignature -FilePath dist\ElectronPackDemo Setup 1.0.0.exe | Select-Object Status, StatusMessage, TimeStamperCertificate输出里 Status 必须是 ValidTimeStamperCertificate 不能为空。如果 Status 是 UnknownError基本可以断定是签名或时间戳没打上不用等测试反馈就能提前发现。把这个命令写进 CI 流水线的后置步骤比默默发版再被用户提醒要省心得多。另一个复用技巧是把缓存目录纳入备份策略。我在团队内部约定新入职同事的机器先同步一次 %LOCALAPPDATA%\electron-builder\Cache之后打包的时间能稳定控制在 3 分钟左右而不需要反复等待几十 MB 的下载。缓存一旦就位electron-v8.5.5-win32-x64、nsis-3.0.4.2、winCodeSign-2.6.0 三个包就是本地常驻资源打包变成了真正的离线流程。做 Electron 打包时间久了最大的教训是版本和证书这两样东西不能靠“感觉”。版本锁不准缓存就开始膨胀慢则多下载几个包快则升级后报一个看不懂的编译错证书管理不严肃密码写进配置里再推到共享仓库等于把签名钥匙交给了所有人。我自己在这方面吃过亏——密码从 package.json 挪到环境变量只花了我十分钟却把一个隐患彻底关掉了。个人开发时那种“怎么方便怎么来”的松弛感带到打包流水线上关键时刻会咬人。希望这些经验帮到你让你在遇到这三个绿色文件夹时心里有底。本文还有配套的精品资源点击获取