桌面应用开发:Electron 与 NSIS 的关系、打包流程及 Windows 本地构建实战

桌面应用开发: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 负责编译安装程序,但中间还需要一个工具完成以下工作:

  1. 读取项目配置;
  2. 收集应用文件;
  3. 处理生产环境依赖;
  4. 下载并整理目标平台的 Electron 运行时;
  5. 生成 Windows 应用目录;
  6. 准备应用图标和元数据;
  7. 调用 NSIS;
  8. 输出最终安装程序。

这个中间工具可以是 electron-builder。

electron-builder 是 Electron 应用构建与分发工具,Windows 平台的默认目标之一就是 NSIS 安装程序 。它还支持 nsis-webportable、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 installnpm 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, // 只向页面提供当前操作系统平台名称
}); // 结束桌面接口定义

这里没有把整个 processrequire 或文件系统模块交给页面,而是只暴露一个字符串。

暴露给渲染页面的接口应保持最小化,只提供页面实际需要的操作和数据

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 配置支持一键安装、向导安装、当前用户安装、所有用户安装、安装目录选择、桌面快捷方式、开始菜单快捷方式和自定义安装脚本等选项。默认情况下,oneClicktrue;只有关闭一键安装后,安装目录选择等向导功能才具有实际意义。

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_MIRRORelectron_mirror、Electron 缓存
nsis-*.7zwinCodeSign-*.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 等配置,但是否删除用户数据必须根据产品行为决定。

卸载时直接删除用户数据可能导致:

  • 用户配置丢失;
  • 本地数据库丢失;
  • 离线文件丢失;
  • 日志和诊断信息丢失;
  • 用户重新安装后无法恢复状态。

更合理的策略是:

  1. 普通卸载默认保留用户数据;
  2. 安装向导中提供明确选择;
  3. 删除前提示具体目录;
  4. 不删除应用目录之外的未知文件;
  5. 不使用模糊通配符递归删除用户目录。

十一、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-*.7zwinCodeSign-*.7z,重点检查 electron-builder 工具链镜像和 %LOCALAPPDATA%\electron-builder\Cache,而不是反复修改业务代码。

可以先移除自定义 installer.nshinclude 配置,验证默认 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 不要把敏感信息写入前端代码

以下信息不能因为应用被打包成 .exeapp.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 相关资源都能下载成功。正确做法是分别检查:

  1. npm registry 是否生效;
  2. Electron 镜像是否生效;
  3. electron-builder 工具链镜像是否生效;
  4. 缓存中是否存在损坏或旧版本文件;
  5. win-unpacked 是否可以直接运行;
  6. 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/

相关推荐
我要两颗404西柚1 小时前
Stage three:VUE工程化与实战工具
前端·javascript·vue.js
写不来代码的草莓熊2 小时前
Cesium 地图交互绘制圆形、多边形
前端·javascript·地图
脚踏实地皮皮晨2 小时前
003002004_WPF Panel 基类 官方类定义
开发语言·windows·算法·c#·wpf·visual studio
sukalot3 小时前
windows网络适配器驱动开发-双 STA 连接(下)
windows·驱动开发·stm32
JackSparrow4143 小时前
前端安全之JS混淆+请求加密+请求签名以提升爬虫难度
前端·javascript·后端·爬虫·python·安全
IT小盘13 小时前
12-Prompt不等于一句话-System-User-Context三层结构
java·windows·prompt
kyriewen16 小时前
我用AI写了半年代码——回头看,这5个能力正在退化
前端·javascript·ai编程
大龄秃头程序员17 小时前
iOS 客户端视角扫盲:WKWebView 里 window、messageHandlers 与 Native 回调到底怎么工作?
javascript