桌面应用开发:Electron 与 NSIS 的关系、打包流程及 Windows 本地构建实战
Electron 可以使用 JavaScript、超文本标记语言(HyperText Markup Language, HTML)和层叠样式表(Cascading Style Sheets, CSS)开发桌面应用,但"应用能够在开发环境中启动"与"应用能够交付给普通用户安装"属于两个不同阶段。Electron 负责窗口、进程和系统能力,electron-builder 负责整理应用代码、依赖与 Electron 运行时,Nullsoft脚本化安装系统(Nullsoft Scriptable Install System, NSIS)则负责把构建结果封装成 Windows 安装程序。
Electron 解决的是"桌面应用如何运行",electron-builder 解决的是"项目如何形成可分发产物",NSIS 解决的是"应用如何安装、升级和卸载" 。在 Windows 本地构建时,开发者通常会先生成
win-unpacked目录,验证其中的可执行文件能够正常启动,再由 NSIS 生成最终的Setup.exe安装包。国内网络环境下,构建失败经常并非代码错误,而是下载链路没有配置完整。npm 包、Electron 预编译二进制文件以及 electron-builder 所需的 NSIS、WinCodeSign 等工具链资源,可能来自不同地址。只切换 npm registry,并不能保证 Electron 二进制和 electron-builder 工具链都能成功下载。因此,Windows 本地构建还需要分别配置 npm registry、Electron 镜像和 electron-builder 二进制镜像,并结合缓存目录、下载日志与构建产物逐层排查。
本文围绕 Electron、electron-builder 与 NSIS 的职责边界,介绍 Electron 进程模型、Windows 本地项目创建、国内镜像配置、NSIS 安装选项、自定义安装脚本、构建验证、常见下载故障和安全边界。
一、Electron 与 NSIS 的基本概念
1.1 Electron 是什么
Electron 是一个使用 Web 技术开发桌面应用的框架,其核心组合包括 Chromium 渲染引擎、Node.js 运行环境以及桌面系统接口。
传统 Web 应用主要运行在浏览器中,通常无法直接完成以下操作:
- 创建原生桌面窗口;
- 访问本地文件系统;
- 创建系统托盘图标;
- 显示原生菜单;
- 调用系统通知;
- 注册快捷键;
- 启动本地进程;
- 与操作系统剪贴板交互;
- 读取应用安装路径和用户数据目录。
Electron 在 Web 页面与操作系统之间增加了一层桌面运行框架,使开发者可以使用熟悉的前端技术构建 Windows、Linux 和 macOS 桌面应用。
不过,Electron 应用并不是"把网页改成 .exe"这么简单。一个完整的 Electron 应用通常包含:
| 组成部分 | 主要职责 |
|---|---|
| 主进程 | 管理应用生命周期、窗口、菜单和系统能力 |
| 渲染进程 | 显示 HTML、CSS 和 JavaScript 用户界面 |
| 预加载脚本 | 在受控范围内连接主进程能力与渲染页面 |
| Electron 运行时 | 提供 Chromium、Node.js 和桌面接口 |
| 应用资源 | 页面、脚本、图标、配置和其他静态文件 |
| 构建配置 | 决定如何整理、压缩和生成安装产物 |
Electron 使用多进程模型。主进程负责应用生命周期和具有较高权限的操作,每个 BrowserWindow 通常对应独立的渲染进程。渲染页面需要调用桌面能力时,应通过预加载脚本和进程间通信(Inter-Process Communication, IPC)建立受控接口,而不是直接把完整的 Node.js 能力暴露给页面。
1.2 NSIS 是什么
NSIS 是一个用于创建 Windows 安装程序的开源脚本化工具。
NSIS 可以完成:
- 将应用文件释放到安装目录;
- 创建桌面快捷方式;
- 创建开始菜单快捷方式;
- 写入或删除注册表;
- 判断操作系统架构;
- 检查已有版本;
- 执行覆盖安装;
- 注册卸载程序;
- 删除旧文件;
- 显示安装向导;
- 请求管理员权限;
- 执行自定义安装逻辑。
NSIS 使用自己的脚本语言描述安装流程。它支持变量、函数、条件判断、字符串处理和插件扩展,并且可以控制安装程序中的大部分行为。NSIS 官方将其定义为用于创建 Windows 安装程序的免费开源工具。
1.3 electron-builder 是什么
Electron 负责运行应用,NSIS 负责编译安装程序,但中间还需要一个工具完成以下工作:
- 读取项目配置;
- 收集应用文件;
- 处理生产环境依赖;
- 下载并整理目标平台的 Electron 运行时;
- 生成 Windows 应用目录;
- 准备应用图标和元数据;
- 调用 NSIS;
- 输出最终安装程序。
这个中间工具可以是 electron-builder。
electron-builder 是 Electron 应用构建与分发工具,Windows 平台的默认目标之一就是 NSIS 安装程序 。它还支持 nsis-web、portable、MSI、AppX、压缩包等 Windows 产物。
二、Electron、electron-builder 与 NSIS 的关系
2.1 三者的职责边界
三者可以理解为以下关系:

text
Electron 源代码
↓
electron-builder 收集应用文件和运行时
↓
生成可直接运行的 Windows 应用目录
↓
NSIS 将应用目录封装成安装程序
↓
输出 Windows 安装包
它们分别回答三个问题:
| 工具 | 解决的问题 |
|---|---|
| Electron | 应用如何创建窗口并调用系统能力 |
| electron-builder | 项目如何整理为目标平台的可分发应用 |
| NSIS | Windows 用户如何安装、升级和卸载应用 |
2.2 "打包"和"制作安装程序"不是一回事
这两个概念经常被混用。
应用打包,是将源代码、运行时和依赖整理成可以运行的应用目录;安装程序制作,是将已经整理好的应用目录封装成便于分发和安装的文件。
假设应用名称为 Electron NSIS Demo,构建过程可能产生两类结果:
text
dist/
├── win-unpacked/
│ ├── Electron NSIS Demo.exe
│ ├── resources/
│ ├── locales/
│ └── 其他 Electron 运行文件
└── Electron NSIS Demo-Setup-1.0.0.exe
其中:
win-unpacked是已经打包但尚未封装为安装向导的应用目录;Electron NSIS Demo-Setup-1.0.0.exe是 NSIS 生成的安装程序;- 前者可以直接用于测试;
- 后者适合交付给最终用户。
2.3 应用代码与安装逻辑应当分离
Electron 代码负责应用运行时行为,例如窗口、菜单、数据读写和网络请求。
NSIS 配置负责安装阶段行为,例如:
- 安装到哪里;
- 是否需要管理员权限;
- 是否创建快捷方式;
- 是否允许用户选择安装路径;
- 卸载时是否删除用户数据;
- 安装完成后是否自动启动应用。
不要把安装目录创建、注册表写入和快捷方式管理等逻辑放进 Electron 的业务代码中,也不要让 NSIS 脚本承担应用运行期间的业务功能。
三、Electron 应用的进程模型

3.1 主进程
主进程是 Electron 应用的入口进程,通常负责:
- 初始化应用;
- 创建浏览器窗口;
- 监听应用退出事件;
- 管理系统托盘;
- 调用菜单、文件选择器和系统通知;
- 处理来自渲染进程的 IPC 请求;
- 执行需要 Node.js 权限的操作。
每个 Electron 应用通常只有一个主进程。
3.2 渲染进程
渲染进程负责展示界面,工作方式接近普通浏览器页面。
它主要处理:
- HTML 页面;
- CSS 样式;
- 页面交互;
- 前端框架;
- 表单;
- 用户界面状态;
- 页面事件。
渲染进程不应该默认拥有完整的文件系统、进程执行和操作系统访问能力。
3.3 预加载脚本
预加载脚本在页面加载前执行,用于向渲染页面暴露经过筛选的桌面能力。
例如,页面可能只需要知道当前操作系统类型,那么预加载脚本可以只暴露:
text
desktopAPI.platform
而不是暴露:
text
require
process
fs
child_process
ipcRenderer
预加载脚本的核心价值,是将"页面需要什么能力"和"Node.js 能做什么事情"分离开来。
3.4 上下文隔离
上下文隔离(Context Isolation)会将预加载脚本运行环境和页面 JavaScript 环境分开。
Electron 官方建议:
- 启用上下文隔离;
- 启用渲染进程沙箱;
- 不为远程内容启用 Node.js 集成;
- 不关闭
webSecurity; - 使用严格的内容安全策略;
- 不向页面暴露完整 Electron 接口;
- 对 IPC 消息发送者进行验证。
四、在 Windows 中创建 Electron 项目
4.1 准备 Windows 本地开发环境
Windows 本地构建 Electron 应用,至少需要以下基础环境:
- Windows 10 或 Windows 11;
- Node.js 长期支持版本;
- npm;
- PowerShell;
- 一个能够正常读写的项目目录;
- 如果依赖中包含需要本地编译的 Node.js 原生模块,还可能需要 Visual Studio Build Tools 和对应的 Windows 软件开发工具包(Windows Software Development Kit, Windows SDK)。
需要特别区分两种场景:普通 Electron 应用打包使用的是官方预编译 Electron 运行时,一般不需要自行编译 Chromium 或 Electron;只有项目依赖包含原生扩展,并且没有匹配当前 Electron 版本与处理器架构的预编译二进制文件时,才可能需要 C++ 构建工具链。
以下命令在 Windows PowerShell 中执行:
powershell
node --version # 查看当前 Node.js 版本,确认 Node.js 已正确加入 PATH
npm --version # 查看当前 npm 版本,确认 npm 命令可以正常运行
Get-ExecutionPolicy -List # 查看 PowerShell 执行策略,排查 npm.ps1 被阻止的问题
如果 PowerShell 提示无法运行 npm.ps1,可以先使用 npm.cmd 验证 Node.js 是否正常,例如:
powershell
npm.cmd --version # 绕过 npm.ps1,直接调用 Windows 命令脚本验证 npm
执行策略是否需要调整,应结合本机安全要求判断。不要为了省事将整个系统长期设置为完全不受限制。
4.2 初始化项目
以下命令在 Windows PowerShell 中执行:
powershell
New-Item -ItemType Directory -Path electron-nsis-demo -Force | Out-Null # 创建 Electron 示例项目目录
Set-Location electron-nsis-demo # 进入项目目录
npm init -y # 创建基础 package.json 文件
npm install --save-dev electron electron-builder # 安装 Electron 和 electron-builder 开发依赖
安装完成后,在 Windows PowerShell 的项目根目录 中执行:
powershell
node --version # 查看项目使用的 Node.js 运行环境
npm --version # 查看 npm 版本
npx electron --version # 查看 Electron 是否安装成功及其版本
npx electron-builder --version # 查看 electron-builder 是否安装成功及其版本
4.3 配置项目入口与构建命令
为避免直接编辑 package.json 时出现 JSON 逗号、引号或转义错误,可以使用 npm 命令写入脚本。
以下命令在 Windows PowerShell 的项目根目录 中执行:
powershell
npm pkg set main="main.cjs" # 将 Electron 主进程入口设置为 main.cjs
npm pkg set scripts.start="electron ." # 添加开发环境启动命令
npm pkg set scripts.pack-win="electron-builder --config electron-builder.config.cjs --win --dir" # 添加生成 win-unpacked 的命令
npm pkg set scripts.dist-win="electron-builder --config electron-builder.config.cjs --win nsis" # 添加生成 NSIS 安装包的命令
这里将 Windows 构建拆成两个命令:
npm run pack-win:只生成可直接运行的win-unpacked目录,便于先排查应用打包问题;npm run dist-win:继续调用 NSIS,生成最终安装程序。
先检查 win-unpacked,再生成 NSIS 安装包,可以把"应用本身无法运行"和"安装程序配置错误"分开排查。
4.4 国内网络环境下配置 npm 与 Electron 镜像
Electron 项目安装和打包过程中可能涉及三类下载:
| 下载内容 | 常见触发阶段 | 典型配置入口 |
|---|---|---|
| JavaScript 依赖包 | npm install、npm ci |
npm registry |
| Electron 预编译二进制文件 | 安装 electron 或重新下载运行时 |
ELECTRON_MIRROR / electron_mirror |
| electron-builder 工具链资源 | 首次生成 NSIS、签名或其他目标 | ELECTRON_BUILDER_BINARIES_MIRROR / electron_builder_binaries_mirror |
npm registry 只负责 npm 包仓库,不会自动接管 Electron 和 electron-builder 的全部二进制下载 。因此,只执行 npm config set registry 后仍然卡在 GitHub 下载,并不矛盾。
首先在 Windows PowerShell 中切换 npm 包仓库:
powershell
npm config set registry https://registry.npmmirror.com/ # 将 npm 包下载地址切换为国内镜像
npm config get registry # 查看当前生效的 npm registry
npm ping # 测试当前 npm registry 是否可以访问
更推荐在项目根目录创建 .npmrc,将与本项目相关的镜像配置固定下来:
ini
# npm JavaScript 依赖包镜像
registry=https://registry.npmmirror.com/
# Electron 预编译二进制镜像
electron_mirror=https://npmmirror.com/mirrors/electron/
# electron-builder 工具链镜像
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
.npmrc 是 INI 风格配置文件。项目级 .npmrc 放在项目根目录,只影响当前项目及其 npm 命令,比修改所有项目共享的全局配置更容易审查和迁移。
也可以在当前 PowerShell 会话中设置环境变量:
powershell
$env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/" # 当前终端会话使用 Electron 国内镜像
$env:ELECTRON_BUILDER_BINARIES_MIRROR = "https://npmmirror.com/mirrors/electron-builder-binaries/" # 当前会话使用 electron-builder 工具链镜像
npm install # 在当前会话中重新安装依赖
当前会话环境变量在关闭 PowerShell 后失效,适合临时验证。项目级 .npmrc 更适合随项目保存,但不应把令牌、密码和私有仓库凭据直接提交到公共代码仓库。
4.5 镜像切换后仍然下载失败怎么办
镜像切换后仍然失败,常见原因包括:
- 之前失败的压缩包已经进入缓存;
package-lock.json中保留了旧 registry 地址;- 当前终端没有重新加载环境变量;
- 镜像缺少当前版本或当前架构的文件;
- electron-builder 下载的是工具链资源,而不是 Electron 本体;
- 代理、证书检查或企业网络策略拦截了下载;
- 项目版本过旧,对镜像变量的处理存在兼容性差异。
先查看 npm 配置来源。在 Windows PowerShell 的项目根目录 中执行:
powershell
npm config list # 查看当前 npm 配置及其来源
npm config get registry # 再次确认 registry 地址
Get-Content .npmrc # 查看项目级 .npmrc 的实际内容
Electron 官方默认缓存通常位于 %LOCALAPPDATA%\electron\Cache,electron-builder 工具链缓存通常位于 %LOCALAPPDATA%\electron-builder\Cache。不要在不了解内容时直接删除整个用户目录,可以先列出缓存:
powershell
Get-ChildItem "$env:LOCALAPPDATA\electron\Cache" -Force -ErrorAction SilentlyContinue # 查看 Electron 下载缓存
Get-ChildItem "$env:LOCALAPPDATA\electron-builder\Cache" -Force -ErrorAction SilentlyContinue # 查看 electron-builder 工具链缓存
确认缓存中存在失败或不完整文件后,可以只清理对应缓存目录,再重新安装或构建:
powershell
Remove-Item "$env:LOCALAPPDATA\electron\Cache" -Recurse -Force -ErrorAction SilentlyContinue # 清理 Electron 二进制缓存
Remove-Item "$env:LOCALAPPDATA\electron-builder\Cache" -Recurse -Force -ErrorAction SilentlyContinue # 清理 electron-builder 工具链缓存
npm cache verify # 校验 npm 缓存结构
npm ci # 根据 package-lock.json 重新安装固定依赖
如果锁文件中包含旧镜像地址,应先备份并检查,而不是盲目删除。对于正式项目,直接删除 package-lock.json 会改变依赖解析结果,可能引入新的版本差异。
五、编写最小 Electron 应用
5.1 项目目录结构

项目可以整理为:
text
electron-nsis-demo/
├── build/
│ └── icon.ico
├── electron-builder.config.cjs
├── index.html
├── main.cjs
├── package-lock.json
├── package.json
├── preload.cjs
└── renderer.js
其中:
| 文件 | 作用 |
|---|---|
main.cjs |
Electron 主进程入口 |
preload.cjs |
安全暴露桌面接口 |
renderer.js |
页面交互逻辑 |
index.html |
应用界面 |
electron-builder.config.cjs |
Windows 和 NSIS 构建配置 |
build/icon.ico |
Windows 应用及安装程序图标 |
5.2 编写主进程
创建 main.cjs 文件。
javascript
const path = require('node:path'); // 引入 Node.js 路径模块,用于生成跨平台文件路径
const { app, BrowserWindow } = require('electron'); // 引入 Electron 应用对象和窗口对象
app.setAppUserModelId('com.example.electronnsisdemo'); // 设置 Windows 应用用户模型标识,应与构建配置中的 appId 保持一致
function createWindow() { // 定义创建主窗口的函数
const mainWindow = new BrowserWindow({ // 创建一个新的 Electron 浏览器窗口
width: 960, // 设置窗口初始宽度
height: 640, // 设置窗口初始高度
webPreferences: { // 配置窗口中网页的运行环境
preload: path.join(__dirname, 'preload.cjs'), // 指定预加载脚本的绝对路径
nodeIntegration: false, // 禁止页面直接使用 Node.js,降低页面代码获得系统权限的风险
contextIsolation: true, // 启用上下文隔离,将预加载脚本与页面脚本分开
sandbox: true, // 启用渲染进程沙箱,限制页面进程的系统访问能力
}, // 结束网页运行环境配置
}); // 完成主窗口创建
mainWindow.loadFile('index.html'); // 将本地 index.html 加载到主窗口中
} // 结束创建窗口函数
app.whenReady().then(() => { // 等待 Electron 初始化完成
createWindow(); // 创建应用主窗口
app.on('activate', () => { // 监听 macOS 中重新激活应用的事件
if (BrowserWindow.getAllWindows().length === 0) { // 判断当前是否不存在任何窗口
createWindow(); // 在没有窗口时重新创建主窗口
} // 结束窗口数量判断
}); // 结束应用激活事件监听
}); // 结束应用初始化逻辑
app.on('window-all-closed', () => { // 监听所有窗口关闭事件
if (process.platform !== 'darwin') { // 判断当前系统是否不是 macOS
app.quit(); // 在 Windows 和 Linux 中关闭最后一个窗口后退出应用
} // 结束平台判断
}); // 结束所有窗口关闭事件监听
app.setAppUserModelId() 应在创建窗口前执行。在 Windows 中,应用用户模型标识(Application User Model ID, AUMID)会参与通知、任务栏和应用身份识别。electron-builder 文档建议将其设置为与 appId 一致。
5.3 编写预加载脚本
创建 preload.cjs 文件。
javascript
const { contextBridge } = require('electron'); // 引入用于安全暴露接口的 contextBridge 模块
contextBridge.exposeInMainWorld('desktopAPI', { // 在页面的 window 对象中暴露受控的 desktopAPI
platform: process.platform, // 只向页面提供当前操作系统平台名称
}); // 结束桌面接口定义
这里没有把整个 process、require 或文件系统模块交给页面,而是只暴露一个字符串。
暴露给渲染页面的接口应保持最小化,只提供页面实际需要的操作和数据。
5.4 编写渲染页面
创建 index.html 文件。
html
<!doctype html> <!-- 声明当前文档使用 HTML5 标准 -->
<html lang="zh-CN"> <!-- 设置页面语言为简体中文 -->
<head> <!-- 开始页面头部区域 -->
<meta charset="UTF-8"> <!-- 设置页面字符编码为 UTF-8 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <!-- 限制页面只能加载本地资源和本地脚本 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 设置页面视口尺寸 -->
<title>Electron NSIS Demo</title> <!-- 设置应用窗口页面标题 -->
</head> <!-- 结束页面头部区域 -->
<body> <!-- 开始页面主体区域 -->
<h1>Electron 与 NSIS 示例</h1> <!-- 显示页面主标题 -->
<p>当前运行平台:<span id="platform">正在读取</span></p> <!-- 预留显示操作系统名称的位置 -->
<script src="./renderer.js"></script> <!-- 加载渲染进程页面脚本 -->
</body> <!-- 结束页面主体区域 -->
</html> <!-- 结束 HTML 文档 -->
5.5 编写页面脚本
创建 renderer.js 文件。
javascript
const platformElement = document.getElementById('platform'); // 获取用于显示平台名称的页面元素
platformElement.textContent = window.desktopAPI.platform; // 读取预加载脚本暴露的平台信息并显示
5.6 启动应用
以下命令在 Windows PowerShell 的项目根目录 中执行:
powershell
npm run start # 启动 Electron 应用并打开主窗口
如果窗口能够正常显示,并且页面显示 win32,说明主进程、预加载脚本和渲染页面已经正常连接。这里的 win32 是 Node.js 在 Windows 平台上使用的 process.platform 返回值,并不表示应用只能运行在 32 位 Windows 上。
六、配置 electron-builder 和 NSIS
6.1 创建构建配置
创建 electron-builder.config.cjs 文件。
javascript
module.exports = { // 导出 electron-builder 构建配置对象
appId: 'com.example.electronnsisdemo', // 设置稳定且唯一的应用标识,不应在后续版本中随意修改
productName: 'Electron NSIS Demo', // 设置安装程序、快捷方式和应用显示名称
asar: true, // 将主要应用代码整理到 ASAR 归档中
directories: { // 配置构建资源目录和输出目录
output: 'dist', // 将所有构建产物输出到 dist 目录
buildResources: 'build', // 从 build 目录读取图标和 NSIS 资源
}, // 结束目录配置
files: [ // 指定需要包含在最终应用中的文件
'main.cjs', // 包含 Electron 主进程入口
'preload.cjs', // 包含预加载脚本
'renderer.js', // 包含页面交互脚本
'index.html', // 包含应用主页面
'package.json', // 包含应用元数据和依赖声明
], // 结束应用文件列表
win: { // 配置 Windows 平台构建参数
icon: 'build/icon.ico', // 设置 Windows 可执行文件图标
target: [ // 配置 Windows 构建目标
{ // 定义一个 NSIS 构建目标
target: 'nsis', // 使用 NSIS 生成 Windows 安装程序
arch: ['x64'], // 构建 64 位 Windows 应用
}, // 结束 NSIS 目标定义
], // 结束 Windows 构建目标列表
}, // 结束 Windows 平台配置
nsis: { // 配置 NSIS 安装程序行为
oneClick: false, // 关闭一键安装,使用可交互的安装向导
perMachine: false, // 默认允许当前用户安装,避免强制要求管理员权限
allowToChangeInstallationDirectory: true, // 允许用户在安装向导中修改安装目录
createDesktopShortcut: true, // 安装时创建桌面快捷方式
createStartMenuShortcut: true, // 安装时创建开始菜单快捷方式
shortcutName: 'Electron NSIS Demo', // 设置快捷方式显示名称
installerIcon: 'build/icon.ico', // 设置 NSIS 安装程序图标
artifactName: '${productName}-Setup-${version}.${ext}', // 设置最终安装程序文件名
unicode: true, // 使用 Unicode 安装程序以支持中文等多语言字符
runAfterFinish: true, // 安装完成后允许立即启动应用
include: 'build/installer.nsh', // 加载自定义 NSIS 扩展脚本
}, // 结束 NSIS 配置
}; // 结束完整构建配置
electron-builder 的 NSIS 配置支持一键安装、向导安装、当前用户安装、所有用户安装、安装目录选择、桌面快捷方式、开始菜单快捷方式和自定义安装脚本等选项。默认情况下,oneClick 为 true;只有关闭一键安装后,安装目录选择等向导功能才具有实际意义。
6.2 appId 为什么不能随意修改
appId 是应用的稳定身份标识。
它会影响:
- Windows 应用身份;
- AUMID;
- 安装程序生成的确定性标识;
- 版本升级识别;
- 快捷方式;
- 通知图标;
- 旧版本覆盖安装。
electron-builder 可以根据 appId 生成安装程序使用的全局唯一标识符(Globally Unique Identifier, GUID)。如果发布后修改 appId,旧版本和新版本可能被 Windows 识别成两个不同应用,导致静默升级、覆盖安装或卸载关系异常。
6.3 productName 与 package.json 中 name 的区别
name 通常用于:
- npm 包名称;
- 内部项目名称;
- 默认可执行文件名称的一部分;
- 构建工具内部识别。
productName 通常用于:
- 安装向导标题;
- 开始菜单名称;
- 桌面快捷方式名称;
- Windows 应用显示名称;
- 最终产物名称。
建议:
text
name: electron-nsis-demo
productName: Electron NSIS Demo
内部名称应保持小写、简洁并避免空格,用户可见名称则可以包含空格和中文。
七、理解 NSIS 的安装模式
7.1 一键安装与向导安装
NSIS 安装程序主要有两种交互方式。
| 模式 | 配置 | 主要特点 |
|---|---|---|
| 一键安装 | oneClick: true |
操作简单,安装过程较少询问 |
| 向导安装 | oneClick: false |
可展示安装步骤、目录和安装范围选项 |
需要允许用户选择安装目录时,应使用向导安装模式。
7.2 当前用户安装
当前用户安装通常写入用户目录,例如:
text
C:\Users\用户名\AppData\Local\Programs\应用名称
特点包括:
- 通常不需要管理员权限;
- 只对当前 Windows 用户可见;
- 不影响计算机上的其他账户;
- 更适合普通桌面工具;
- 升级和卸载权限关系更简单。
对应配置:
javascript
perMachine: false, // 使用当前用户安装模式,避免默认写入系统级目录
7.3 所有用户安装
所有用户安装通常写入:
text
C:\Program Files\应用名称
特点包括:
- 对计算机上的多个用户可用;
- 通常需要用户账户控制(User Account Control, UAC)授权;
- 更适合统一部署的软件;
- 权限和升级处理更复杂;
- 不应将运行数据写入安装目录。
对应配置:
javascript
perMachine: true, // 使用所有用户安装模式,安装时通常需要管理员权限
7.4 为什么不应把运行数据写入安装目录
应用安装后,程序目录可能位于:
text
C:\Program Files\Electron NSIS Demo
普通用户对该目录通常没有持续写权限。
因此,以下数据不应写入安装目录:
- 用户配置;
- 日志;
- 缓存;
- 下载内容;
- 数据库;
- 登录状态;
- 临时文件;
- 自动更新状态。
Electron 中应使用:
javascript
const { app } = require('electron'); // 引入 Electron 应用对象
const userDataPath = app.getPath('userData'); // 获取当前应用的用户数据目录
安装目录存放程序本体,用户数据目录存放运行期间产生的可变数据 。

八、ASAR 与 NSIS 压缩的区别
8.1 ASAR 是什么
Atom Shell归档格式(Atom Shell Archive, ASAR)是 Electron 使用的一种应用资源归档格式。
应用打包后,源代码通常会被整理到:
text
resources/app.asar
Electron 可以将 ASAR 当作虚拟目录读取,require() 和部分文件读取接口可以直接访问其中内容。Electron 官方文档指出,ASAR 常用于将应用源代码整理为单一归档,并改善 Windows 长路径和文件读取问题。
8.2 ASAR 不等于加密
ASAR 只是归档格式,不是源代码加密方案。
它能实现:
- 减少零散文件数量;
- 改善部分文件读取;
- 避免普通用户直接看到大量源码文件;
- 简化资源目录结构。
它不能实现:
- 阻止专业人员提取源码;
- 保护硬编码密钥;
- 替代代码签名;
- 替代后端权限控制;
- 阻止恶意篡改。
任何不能公开的密钥、令牌和核心权限判断,都不应依赖 ASAR 隐藏。
8.3 ASAR 是只读归档
Electron 官方文档明确说明,ASAR 归档在运行期间不能直接修改。
因此,下面的设计是不正确的:
text
resources/app.asar/config.json
应用不能把用户设置持续写回这个文件。
正确做法是:
text
安装资源中的默认配置
↓
应用第一次启动
↓
复制到 userData 目录
↓
后续只修改 userData 中的配置
8.4 NSIS 压缩解决的是安装包大小
ASAR 负责整理应用内部资源,NSIS 压缩负责缩小安装程序。
两者作用层级不同:
| 技术 | 处理对象 | 主要作用 |
|---|---|---|
| ASAR | Electron 应用资源 | 归档代码和静态资源 |
| NSIS 压缩 | 整个安装包 | 压缩应用目录和安装资源 |
九、在 Windows 本地构建 NSIS 安装程序
9.1 Windows 本地构建的完整链路
Windows 本地构建不需要 Wine,也不需要通过 Linux 容器模拟 Windows 工具链。构建过程可以概括为:
text
Windows 项目目录
↓
npm ci 安装锁定依赖
↓
electron-builder 收集应用文件和 Electron 运行时
↓
生成 dist\win-unpacked
↓
直接运行应用进行检查
↓
调用 NSIS 工具链
↓
生成 Setup.exe 安装程序
Windows 本地构建最大的优势,是生成目标、原生依赖、安装器和最终测试环境保持一致。如果项目使用 Windows 动态链接库、原生 Node.js 模块、注册表、系统服务或代码签名,本地构建通常比跨平台构建更容易定位问题。
9.2 为什么推荐先执行 npm ci
npm ci 会根据 package-lock.json 安装确定的依赖版本,并在安装前清理现有 node_modules。相比直接执行 npm install,它更适合可重复构建:
- 不主动重写锁文件;
- 依赖版本更加确定;
- 本地开发机与持续集成环境更容易保持一致;
- 更容易判断故障来自网络、依赖还是构建配置。
以下命令在 Windows PowerShell 的项目根目录 中执行:
powershell
npm ci # 按 package-lock.json 重新安装确定版本的依赖
npm run pack-win # 先生成 dist\win-unpacked 目录
如果项目尚未生成 package-lock.json,需要先通过一次经过审查的 npm install 创建锁文件,再将其纳入版本管理。
9.3 先验证 win-unpacked
执行 npm run pack-win 后,通常会生成:
text
dist/
└── win-unpacked/
├── Electron NSIS Demo.exe
├── resources/
├── locales/
└── 其他 Electron 运行文件
在 Windows PowerShell 的项目根目录 中执行:
powershell
Get-ChildItem .\dist\win-unpacked -Force # 查看解压构建目录中的实际文件
& ".\dist\win-unpacked\Electron NSIS Demo.exe" # 直接启动未封装为安装程序的应用
如果这里已经白屏、闪退或资源缺失,说明问题发生在 Electron 应用打包阶段,不应继续把注意力放在 NSIS 安装向导上。
9.4 生成 NSIS 安装程序
确认 win-unpacked 可以正常运行后,在 Windows PowerShell 的项目根目录 中执行:
powershell
npm run dist-win # 调用 electron-builder 和 NSIS 生成 Windows 安装程序
正常情况下,dist 目录会同时包含应用目录和安装程序,例如:
text
dist/
├── win-unpacked/
└── Electron NSIS Demo-Setup-1.0.0.exe
生成安装程序时,electron-builder 可能首次下载 NSIS、WinCodeSign 或其他构建工具。此时即使 Electron 本体已经安装成功,仍可能出现新的下载阶段。因此,日志中的下载文件名和地址非常关键。

9.5 下载日志如何判断问题属于哪一层
构建失败时,应关注日志正在下载什么:
| 日志中的对象 | 对应层次 | 优先检查 |
|---|---|---|
| 普通 npm 包压缩包 | npm registry | npm config get registry、锁文件地址 |
electron-v版本-win32-x64.zip |
Electron 运行时 | ELECTRON_MIRROR、electron_mirror、Electron 缓存 |
nsis-*.7z、winCodeSign-*.7z 等 |
electron-builder 工具链 | ELECTRON_BUILDER_BINARIES_MIRROR、工具链缓存 |
| Node.js 原生模块编译日志 | 本地编译工具链 | Visual Studio Build Tools、Python、Windows SDK、架构与 Electron 版本 |
不要把所有下载失败都归结为 npm 镜像未生效,也不要看到 NSIS 下载失败就误判为安装脚本语法错误。
9.6 构建产物与缓存应如何管理
dist 是构建结果,缓存目录用于避免重复下载,两者用途不同:
dist可以在每次正式构建前清理;- Electron 与 electron-builder 缓存不应无理由频繁删除;
- 构建失败后,应先根据日志定位具体缓存项;
- 团队内部可以保留经过验证的缓存,以减少重复下载;
- 离线环境可以从联网机器复制完整缓存,但必须确保版本与架构匹配。
以下命令在 Windows PowerShell 的项目根目录 中执行:
powershell
Remove-Item .\dist -Recurse -Force -ErrorAction SilentlyContinue # 清理旧构建产物,避免误把旧文件当作本次结果
npm run pack-win # 重新生成未封装应用目录
npm run dist-win # 重新生成 NSIS 安装程序
十、自定义 NSIS 安装行为
10.1 优先使用 include,而不是完全替换脚本
electron-builder 提供两种主要自定义方式:
include:加载附加 NSIS 宏;script:使用完整自定义 NSIS 脚本替换默认脚本。
官方文档建议,大多数情况优先使用 include,因为它可以继续使用 electron-builder 已经维护好的默认安装流程,只覆盖必要的扩展点。
10.2 创建 installer.nsh
在 build 目录中创建 installer.nsh:
nsh
!macro customInstall ; 定义安装文件复制完成后执行的自定义安装宏
CreateDirectory "$APPDATA\ElectronNsisDemo" ; 在当前用户的应用数据目录中创建应用数据文件夹
DetailPrint "已初始化 ElectronNsisDemo 用户数据目录" ; 在安装详细信息中输出初始化结果
!macroend ; 结束自定义安装宏
!macro customUnInstall ; 定义卸载过程中执行的自定义卸载宏
DetailPrint "默认保留用户配置和运行数据" ; 明确说明卸载时不主动删除用户数据
!macroend ; 结束自定义卸载宏
这里创建的是用户数据目录,而不是在安装目录中创建可写业务目录。
10.3 不要默认删除用户数据
electron-builder 提供 deleteAppDataOnUninstall 等配置,但是否删除用户数据必须根据产品行为决定。
卸载时直接删除用户数据可能导致:
- 用户配置丢失;
- 本地数据库丢失;
- 离线文件丢失;
- 日志和诊断信息丢失;
- 用户重新安装后无法恢复状态。
更合理的策略是:
- 普通卸载默认保留用户数据;
- 安装向导中提供明确选择;
- 删除前提示具体目录;
- 不删除应用目录之外的未知文件;
- 不使用模糊通配符递归删除用户目录。
十一、NSIS、NSIS Web 与便携版的区别
electron-builder 支持多种 Windows 目标。
| 目标 | 产物 | 特点 | 适用场景 |
|---|---|---|---|
nsis |
完整 .exe 安装包 |
包含应用文件,可离线安装 | 普通软件分发 |
nsis-web |
较小的在线安装器 | 安装时下载对应架构应用包 | 应用体积较大 |
portable |
便携式 .exe |
不写入传统安装信息 | 临时运行、便携工具 |
msi |
Windows Installer 包 | 适合部分企业部署体系 | 组织统一安装 |
dir |
解压后的应用目录 | 不生成安装程序 | 调试和检查 |
NSIS Web 安装器会在安装时根据系统架构下载对应应用包,因此安装器自身更小,但它依赖网络、下载服务器和包校验。普通 nsis 安装包则包含所需应用文件,更适合离线场景。
十二、Windows 代码签名
12.1 为什么未签名安装程序会出现警告
Windows 无法确认未签名安装程序的发布者身份,因此可能显示:
- 未知发布者;
- Windows 已保护你的电脑;
- 下载文件可能不安全;
- 安装程序来源无法验证。
这不一定代表安装程序包含恶意代码,但会明显影响用户信任和安装成功率。
12.2 代码签名解决什么问题
代码签名可以帮助系统和用户确认:
- 安装程序由哪个发布者生成;
- 文件签名后是否被修改;
- 发布者证书是否有效;
- 更新包是否来自同一发布者。
Electron 官方建议对面向最终用户分发的桌面应用进行代码签名,并指出 Windows 安装程序的签名是正式分发流程中的重要部分。
12.3 代码签名不等于代码安全
签名只证明:
- 文件来自证书持有者;
- 文件在签名后未发生变化。
它不能证明:
- 应用没有漏洞;
- 应用没有恶意逻辑;
- 依赖不存在供应链问题;
- 数据处理完全安全。
代码签名解决的是身份和完整性问题,不替代安全审计、依赖治理和权限控制。
十三、安装程序的验证方法
13.1 先检查解压目录
NSIS 安装包生成前,通常会产生解压后的应用目录。
应先直接运行其中的应用程序,检查:
- 能否启动;
- 页面是否完整;
- 资源是否缺失;
- 图标是否正确;
- 预加载脚本是否加载;
- 原生模块是否可用;
- 控制台是否报错。
如果解压目录都无法运行,问题通常位于 Electron 打包阶段,而不是 NSIS。
13.2 再检查安装程序
安装程序应至少验证以下内容:
| 检查项目 | 需要确认的结果 |
|---|---|
| 安装启动 | 安装向导能够正常打开 |
| 中文路径 | 安装到含中文路径的位置后能够启动 |
| 空格路径 | 安装目录含空格时运行正常 |
| 当前用户安装 | 不使用管理员权限时能够安装 |
| 所有用户安装 | 能正确请求管理员权限 |
| 桌面快捷方式 | 快捷方式图标和目标路径正确 |
| 开始菜单 | 能找到启动和卸载入口 |
| 安装完成启动 | 应用能够在完成页直接启动 |
| 覆盖安装 | 新版本可以覆盖旧版本 |
| 降级安装 | 是否允许安装旧版本符合预期 |
| 卸载 | 程序文件和快捷方式被正确删除 |
| 用户数据 | 是否保留或删除符合产品规则 |
| 重启系统 | 重启后应用仍然能够正常启动 |
13.3 检查构建产物
以下命令在 Windows PowerShell 的项目根目录 中执行:
powershell
Get-ChildItem .\dist -Recurse -File | Select-Object FullName, Length # 列出 dist 中全部构建文件及其字节大小
计算安装程序的安全哈希算法(Secure Hash Algorithm 256-bit, SHA-256)摘要:
powershell
Get-FileHash ".\dist\Electron NSIS Demo-Setup-1.0.0.exe" -Algorithm SHA256 # 计算安装程序 SHA-256 摘要
发布页面可以同时提供安装包版本、文件大小和 SHA-256 摘要,便于下载者检查文件是否完整。
十四、常见问题与排查方法
14.1 Electron 开发环境可以运行,打包后白屏
常见原因包括:
- 页面资源使用了错误的绝对路径;
- 前端构建目录没有包含在
files中; - 内容安全策略阻止脚本;
- 路由依赖 Web 服务器;
- 预加载脚本路径错误;
- 文件名大小写不一致;
- 开发服务器路径与
file://打包后的本地路径不同;
排查时应先检查 win-unpacked,再检查 NSIS 安装版本。
14.2 renderer 中不能使用 require
如果配置了:
javascript
nodeIntegration: false, // 禁止渲染页面直接访问 Node.js 模块
contextIsolation: true, // 将预加载脚本和页面脚本隔离
渲染页面中就不能直接执行:
javascript
const fs = require('node:fs'); // 该写法不应出现在受隔离的普通渲染页面中
正确方式是:
text
渲染页面发起请求
↓
预加载脚本提供受控方法
↓
主进程执行文件操作
↓
返回有限结果
14.3 allowToChangeInstallationDirectory 不生效
需要同时满足:
javascript
oneClick: false, // 使用可交互的安装向导
allowToChangeInstallationDirectory: true, // 允许用户修改安装目录
如果仍然使用默认一键安装,用户不会看到传统的目录选择页面。
14.4 修改 appId 后出现两个应用
appId 改变后,安装程序可能将新版本识别为另一款应用。
处理方式不是继续手工修改 GUID,而是:
- 从第一次正式发布开始固定
appId; - 测试版和正式版使用明确且稳定的不同标识;
- 不把版本号写进
appId; - 不把构建时间写进
appId; - 升级时保持发布者和应用身份一致。
14.5 安装程序图标没有变化
应检查:
- 文件是否为有效
.ico; - 是否放在配置指定目录;
win.icon是否正确;nsis.installerIcon是否正确;- 构建缓存是否仍在使用旧资源;
- Windows 图标缓存是否尚未刷新。
electron-builder 的 NSIS 安装程序支持独立设置安装器图标;向导式安装还可以配置侧边图和顶部图。
14.6 Windows 本地构建时下载 NSIS 或 WinCodeSign 失败
常见原因包括:
- 只修改了 npm registry,没有配置 electron-builder 工具链镜像;
- 旧缓存中保留了不完整压缩包;
- 当前 PowerShell 会话没有读取新的环境变量;
- 代理或证书检查阻止了 GitHub、镜像站或重定向地址;
- 国内镜像中暂时缺少对应版本;
- electron-builder 版本过旧,对镜像变量的处理存在兼容性差异;
- 自定义 NSIS 脚本语法错误,但日志被前面的下载信息干扰。
排查时先确认下载地址与文件名。如果失败对象是 nsis-*.7z 或 winCodeSign-*.7z,重点检查 electron-builder 工具链镜像和 %LOCALAPPDATA%\electron-builder\Cache,而不是反复修改业务代码。
可以先移除自定义 installer.nsh 的 include 配置,验证默认 NSIS 构建是否能够完成,再逐步恢复自定义宏。这样可以区分"下载工具失败"和"安装脚本编译失败"。
14.7 ASAR 中的文件无法写入
ASAR 是只读归档。需要修改的文件应放在:
app.getPath('userData');app.getPath('documents');app.getPath('temp');- 用户明确选择的目录。
不要尝试修改:
text
resources/app.asar
14.8 安装后写 Program Files 失败
这是权限模型的正常表现,不应通过让应用长期以管理员身份运行来解决。
应将数据迁移到:
text
C:\Users\用户名\AppData\Roaming\应用名称
或者 Electron 返回的 userData 路径。
十五、Electron 与 NSIS 的安全边界
15.1 不要把远程网页当作本地可信页面
如果 Electron 加载远程页面,应特别谨慎。
远程页面内容可能受到:
- 跨站脚本攻击;
- 第三方脚本污染;
- 网络劫持;
- 服务端内容变更;
- 用户输入注入;
- 广告或统计脚本影响。
因此,不应对远程页面开放 Node.js 或完整 Electron 权限。Electron 官方明确建议仅加载安全内容、为渲染器启用上下文隔离和沙箱,并限制导航、新窗口和外部链接调用。
15.2 不要把敏感信息写入前端代码
以下信息不能因为应用被打包成 .exe 或 app.asar 就认为安全:
- 云服务访问密钥;
- 数据库管理员密码;
- 私钥;
- 固定登录令牌;
- 内部服务万能凭据;
- 代码签名证书密码。
Electron 应用运行在用户设备上,用户能够分析安装文件、内存、网络请求和应用资源。
15.3 安装脚本应限制删除范围
NSIS 卸载脚本中的删除操作应满足:
- 使用明确目录;
- 不拼接未经验证的用户输入;
- 不删除父目录;
- 不递归删除共享目录;
- 不删除其他软件数据;
- 删除前确认目录属于本应用;
- 不使用过宽的通配符。
15.4 应用更新必须验证来源
自动更新不是简单下载一个新 .exe 并执行。
需要关注:
- 更新地址是否使用 HTTPS;
- 更新元数据是否可信;
- 更新包是否签名;
- 发布者身份是否一致;
- 是否验证更新代码签名;
- 是否允许任意地址覆盖更新源。
electron-builder 的 Windows 配置支持更新代码签名验证,相关功能依赖稳定的发布者和签名配置。
十六、完整构建流程总结
一个较为可靠的 Electron 与 NSIS 构建流程可以整理为:
#mermaid-svg-iluSKpxdlDhmnTFB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iluSKpxdlDhmnTFB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iluSKpxdlDhmnTFB .error-icon{fill:#552222;}#mermaid-svg-iluSKpxdlDhmnTFB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iluSKpxdlDhmnTFB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iluSKpxdlDhmnTFB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iluSKpxdlDhmnTFB .marker.cross{stroke:#333333;}#mermaid-svg-iluSKpxdlDhmnTFB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iluSKpxdlDhmnTFB p{margin:0;}#mermaid-svg-iluSKpxdlDhmnTFB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iluSKpxdlDhmnTFB .cluster-label text{fill:#333;}#mermaid-svg-iluSKpxdlDhmnTFB .cluster-label span{color:#333;}#mermaid-svg-iluSKpxdlDhmnTFB .cluster-label span p{background-color:transparent;}#mermaid-svg-iluSKpxdlDhmnTFB .label text,#mermaid-svg-iluSKpxdlDhmnTFB span{fill:#333;color:#333;}#mermaid-svg-iluSKpxdlDhmnTFB .node rect,#mermaid-svg-iluSKpxdlDhmnTFB .node circle,#mermaid-svg-iluSKpxdlDhmnTFB .node ellipse,#mermaid-svg-iluSKpxdlDhmnTFB .node polygon,#mermaid-svg-iluSKpxdlDhmnTFB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iluSKpxdlDhmnTFB .rough-node .label text,#mermaid-svg-iluSKpxdlDhmnTFB .node .label text,#mermaid-svg-iluSKpxdlDhmnTFB .image-shape .label,#mermaid-svg-iluSKpxdlDhmnTFB .icon-shape .label{text-anchor:middle;}#mermaid-svg-iluSKpxdlDhmnTFB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iluSKpxdlDhmnTFB .rough-node .label,#mermaid-svg-iluSKpxdlDhmnTFB .node .label,#mermaid-svg-iluSKpxdlDhmnTFB .image-shape .label,#mermaid-svg-iluSKpxdlDhmnTFB .icon-shape .label{text-align:center;}#mermaid-svg-iluSKpxdlDhmnTFB .node.clickable{cursor:pointer;}#mermaid-svg-iluSKpxdlDhmnTFB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iluSKpxdlDhmnTFB .arrowheadPath{fill:#333333;}#mermaid-svg-iluSKpxdlDhmnTFB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iluSKpxdlDhmnTFB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iluSKpxdlDhmnTFB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iluSKpxdlDhmnTFB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iluSKpxdlDhmnTFB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iluSKpxdlDhmnTFB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iluSKpxdlDhmnTFB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iluSKpxdlDhmnTFB .cluster text{fill:#333;}#mermaid-svg-iluSKpxdlDhmnTFB .cluster span{color:#333;}#mermaid-svg-iluSKpxdlDhmnTFB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iluSKpxdlDhmnTFB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iluSKpxdlDhmnTFB rect.text{fill:none;stroke-width:0;}#mermaid-svg-iluSKpxdlDhmnTFB .icon-shape,#mermaid-svg-iluSKpxdlDhmnTFB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iluSKpxdlDhmnTFB .icon-shape p,#mermaid-svg-iluSKpxdlDhmnTFB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iluSKpxdlDhmnTFB .icon-shape .label rect,#mermaid-svg-iluSKpxdlDhmnTFB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iluSKpxdlDhmnTFB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iluSKpxdlDhmnTFB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iluSKpxdlDhmnTFB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 编写 Electron 主进程
编写预加载脚本
编写渲染页面
Windows 开发环境运行检查
配置 electron-builder
配置 Windows 与 NSIS
整理图标和安装资源
执行 Windows 应用打包
检查 win-unpacked
调用 NSIS 生成安装程序
在 Windows 环境安装测试
检查升级和卸载
执行代码签名
发布安装包与哈希值
其中最容易被忽略的是 win-unpacked 检查。
只有解压后的 Windows 应用能够正常运行,才有必要继续排查 NSIS 安装程序;如果应用目录本身无法运行,重新调整安装向导通常没有意义。
十七、总结
Electron、electron-builder 和 NSIS 分别处在 Windows 桌面应用交付链路的不同层级。
Electron 负责:
- 创建桌面窗口;
- 管理主进程和渲染进程;
- 提供系统接口;
- 运行应用业务逻辑。
electron-builder 负责:
- 收集应用代码;
- 整理生产依赖;
- 准备 Windows Electron 运行时;
- 生成
win-unpacked应用目录; - 调用 NSIS 等目标工具。
NSIS 负责:
- 生成 Windows
.exe安装程序; - 释放应用文件;
- 创建快捷方式;
- 控制安装权限;
- 支持升级和卸载;
- 执行有限的自定义安装逻辑。
Electron 应用交付的核心链路,是"应用代码---Windows 应用目录---NSIS 安装程序---安装验证",而不是简单地把网页转换成一个可执行文件。
在 Windows 本地构建时,最容易被忽略的是下载链路的分层。npm registry、Electron 预编译二进制文件和 electron-builder 工具链资源并不一定使用同一个地址。只切换 npm 镜像,无法保证 Electron 和 NSIS 相关资源都能下载成功。正确做法是分别检查:
- npm registry 是否生效;
- Electron 镜像是否生效;
- electron-builder 工具链镜像是否生效;
- 缓存中是否存在损坏或旧版本文件;
win-unpacked是否可以直接运行;- NSIS 安装程序是否能够正常安装、升级和卸载。
实际项目中还应长期坚持以下原则:
- 固定并稳定维护
appId; - 启用上下文隔离和渲染进程沙箱;
- 不向页面暴露完整 Node.js 能力;
- 将运行数据写入用户数据目录;
- 不把 ASAR 当作加密机制;
- 不在卸载脚本中执行过宽的递归删除;
- 使用项目级
.npmrc明确镜像配置; - 先验证
win-unpacked,再排查 NSIS; - 正式发布前完成安装、覆盖升级和卸载测试;
- 对面向用户分发的 Windows 安装程序进行代码签名。
Electron 决定应用能否安全、稳定地运行,electron-builder 决定应用能否形成可分发产物,NSIS 决定应用能否可靠、可控地进入用户的 Windows 系统。
参考资料
1 Electron. Process Model.
https://www.electronjs.org/docs/latest/tutorial/process-model
2 Electron. Security.
https://www.electronjs.org/docs/latest/tutorial/security
3 Electron. Application Packaging.
https://www.electronjs.org/docs/latest/tutorial/application-distribution
4 Electron. Advanced Installation Instructions: Custom Mirrors and Caches.
https://www.electronjs.org/docs/latest/tutorial/installation
5 Electron. ASAR Archives.
https://www.electronjs.org/docs/latest/tutorial/asar-archives
6 electron-builder. Windows Configuration.
https://www.electron.build/docs/win/
7 electron-builder. NSIS Target.
https://www.electron.build/docs/nsis/
8 electron-builder. Command Line Interface.
https://www.electron.build/docs/cli/
9 electron-builder. Troubleshooting and Cache Management.
https://www.electron.build/docs/troubleshooting/
10 electron-builder. Application Contents.
https://www.electron.build/docs/contents/
11 NSIS. Users Manual.
https://nsis.sourceforge.io/Docs/
12 npm. Registry Configuration.
https://docs.npmjs.com/cli/v11/using-npm/registry/
13 npm. npmrc Configuration Files.
https://docs.npmjs.com/cli/v12/configuring-npm/npmrc/
14 Electron. Distribution Overview and Code Signing.
https://www.electronjs.org/docs/latest/tutorial/distribution-overview
15 electron-builder. Code Signing for Windows.
https://www.electron.build/docs/features/code-signing/code-signing-win/