Windows 打包与安装程序
本教程共 42 篇 · 第 34 篇 · 更新于 2026-08-03
34. Windows 打包与安装程序
本节目标
- 理解 WebView2 运行时依赖,会在四种处理策略里做选择
- 会用固定版本 WebView2 运行时,彻底摆脱环境不确定性
- 装好 NSIS 并生成带开始菜单、卸载入口的安装程序
- 会改图标、公司名、版本号这些安装包元数据
- 知道
build/windows目录里每个文件是干什么的
34-1 Windows 产物长什么样
在 Windows 上跑 wails build,build/bin 里会出现一个 exe。双击就能运行,前端资源全在里面。
跟这个 exe 有关的资源文件都放在项目的 build/windows 目录:
build/
├── appicon.png # 通用应用图标源文件
└── windows/
├── icon.ico # Windows 图标,由 appicon.png 自动生成
├── info.json # 版本信息与安装包元数据
├── wails.exe.manifest # 应用清单
└── installer/
├── project.nsi # NSIS 主脚本
└── wails_tools.nsh # Wails 提供的 NSIS 工具宏
icon.ico 不存在时,Wails 会拿 build/appicon.png 自动生成,尺寸覆盖 256、128、64、48、32、16 这几档。想换图标,最省事的做法是替换 appicon.png,删掉 icon.ico,重新构建。
清单文件(manifest)声明的是程序对系统的一些要求,比如 DPI 感知、需要的权限级别。文件不存在时 Wails 会生成一份默认的。
34-2 WebView2 依赖怎么处理
Wails 在 Windows 上用的是微软的 WebView2 渲染界面。这是个运行时依赖——Windows 11 预装了,Windows 10 上部分机器没有。用户机器上没有它,你的程序就打不开。
Wails 给了四种应对策略,用 -webview2 参数指定:
wails build -webview2 download # 默认
wails build -webview2 embed
wails build -webview2 browser
wails build -webview2 error
四种策略的差别:
| 策略 | 检测不到运行时的行为 | 体积影响 |
|---|---|---|
download | 提示用户,然后下载并运行微软官方引导安装器 | 无 |
embed | 引导安装器已内置在程序里,直接提示运行 | 约 +150KB |
browser | 提示用户,打开浏览器到官方下载页,程序退出 | 无 |
error | 报错并结束,不做任何补救 | 无 |
怎么选?给个实际建议:
- 面向普通用户分发 →
embed。150KB 换一个「离线也能装」的体验很划算。download在断网或公司内网限制外网的机器上会失败。 - 企业内部工具,环境可控 →
error也行,出问题能第一时间暴露,而不是静默弹一个用户看不懂的安装器。 - 想让用户自己决定 →
browser。
Note这几种策略不只处理「完全没装」的情况,运行时版本太旧同样会触发。所以别觉得「测试机上有就万事大吉」。
34-3 固定版本运行时
还有第五条路:自己带一份 WebView2 运行时,不依赖用户机器上的。微软把这种模式叫「固定版本运行时」(Fixed Version Runtime)。
好处是渲染行为完全确定,不会因为用户机器上 WebView2 自动更新而变样。代价是体积——完整运行时上百 MB。
用法分两步。先从微软官网下载固定版本运行时包,它是 .cab 格式,需要解压:
expand 运行时包路径 -F:* 目标文件夹
然后在 Go 代码里告诉 Wails 去哪里找它:
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/options/windows"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "MyApp",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Windows: &windows.Options{
WebviewBrowserPath: "./runtime/webview2",
},
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}
Warning指定了
WebviewBrowserPath之后,一旦路径无效或者运行时版本低于最低要求,Wails 会强制走error策略——直接报错退出,不会回退到下载或浏览器方案。路径写错的后果是程序打不开,务必测一遍。
这条路适合对渲染一致性要求高的场景,比如做图形编辑、报表打印这类对 CSS 细节敏感的应用。普通业务应用用 embed 就够了。
34-4 安装 NSIS
只发一个 exe,用户下载完随便丢在哪里,没有开始菜单项,也没有卸载入口。要解决这些,得做安装程序。Wails 内置支持 NSIS(Nullsoft Scriptable Install System),一个老牌的 Windows 安装包制作工具。
Windows 上安装 NSIS,三选一:
# Scoop(会自动加进 PATH)
scoop bucket add extras
scoop install nsis
# Winget(Windows 10 及以上)
winget install NSIS.NSIS --silent
# Chocolatey
choco install nsis
在别的系统上也能装,用于 CI 场景:
# Linux(用发行版自带包管理器)
sudo apt install nsis
# macOS
brew install nsis
Warning手动下载安装包装 NSIS 的话,记得把安装目录下的
Bin文件夹(里面有makensis.exe)加进系统 PATH。不加的话wails build -nsis会找不到makensis,报错信息不太直观,很容易卡在这里。
34-5 生成安装程序
装好 NSIS,加个参数就行:
wails build -nsis
安装包同样输出到 build/bin 目录,文件名形如 MyApp-amd64-installer.exe。
安装包里的公司名、产品名、版本号这些信息,来自 wails.json 的 info 段:
{
"name": "myapp",
"outputfilename": "MyApp",
"info": {
"companyName": "码上学工作室",
"productName": "My Wails App",
"productVersion": "1.2.0",
"copyright": "Copyright © 2026 码上学",
"comments": "使用 Wails 构建"
}
}
这几个字段不只进安装包,也会写进 exe 的文件属性——用户右键看「属性 → 详细信息」时显示的就是它们。填好了显得专业,留着默认的 Copyright......... 就有点糊弄了。
还有一个字段控制安装包的打包方式:
{
"nsisType": "multiple"
}
multiple(默认):每个架构一个安装包,amd64 和 arm64 分开single:所有已构建架构合并成一个通用安装包
同时发 amd64 和 arm64 时,single 能让用户只面对一个下载链接,安装器自动挑对的架构装。代价是包体积大概翻倍。
Tip想深度定制安装流程(加许可协议页、自定义安装选项、写注册表),改
build/windows/installer/project.nsi就行。这是一份标准的 NSIS 脚本,Wails 只是把常用逻辑抽进了wails_tools.nsh。改之前先备份,NSIS 脚本语法有点古老。
34-6 一个容易踩的运行期细节
Windows 版还有个跟打包无关、但几乎人人会遇到的坑:从 Wails 应用里启动外部程序时,会闪出一个黑色控制台窗口。
用户看到界面上突然闪一下黑框,体验很差。解决办法是给子进程加上隐藏窗口的标志:
package main
import (
"os/exec"
"syscall"
)
func runHidden(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true,
CreationFlags: 0x08000000, // CREATE_NO_WINDOW
}
return cmd.Start()
}
Note
syscall.SysProcAttr的字段是平台相关的,HideWindow只在 Windows 上存在。这段代码放在带_windows.go后缀的文件里,配一个_other.go兜底,才能保证三端都编得过。
常见误区
默认 download 策略在内网环境失效。 不少企业机器访问不了微软的下载地址。面向这类环境分发,用 embed。
只在 Windows 11 上测过就发版。 Windows 11 预装 WebView2,测不出依赖缺失的问题。找一台干净的 Windows 10 虚拟机验证一遍。
改了 appicon.png 但图标没变。 因为 build/windows/icon.ico 已经存在,Wails 不会重新生成。删掉 ico 再构建。
nsisType 设成 single 却只编了一个架构。 那跟 multiple 没区别。single 的前提是 -platform windows/amd64,windows/arm64 一次编两个架构。
忘了给 exe 签名。 未签名的安装包在 Windows 上会弹 SmartScreen 警告,用户要点「更多信息 → 仍要运行」才能装。代码签名的做法下一章顺带提一下。
小结
Windows 打包要处理的核心问题就是 WebView2 依赖。四种策略里,面向普通用户优先 embed,对渲染一致性有硬要求就上固定版本运行时。
安装程序靠 NSIS,装好工具后 wails build -nsis 一条命令搞定,元数据在 wails.json 的 info 段配置。要深度定制就改 project.nsi。
图标从 build/appicon.png 自动转,改了不生效多半是旧的 icon.ico 还在。
下一章换到 macOS,那边的重点从「运行时依赖」变成了「签名与公证」——不签名的 app 在别人机器上根本打不开。