首页 / Wails 入门教程 / Windows 打包与安装程序

Wails 入门教程

Windows 打包与安装程序

本教程共 42 篇 · 第 34 篇 · 更新于 2026-08-03

Wails桌面开发WindowsNSISWebView2打包

34. Windows 打包与安装程序

本节目标

  • 理解 WebView2 运行时依赖,会在四种处理策略里做选择
  • 会用固定版本 WebView2 运行时,彻底摆脱环境不确定性
  • 装好 NSIS 并生成带开始菜单、卸载入口的安装程序
  • 会改图标、公司名、版本号这些安装包元数据
  • 知道 build/windows 目录里每个文件是干什么的

34-1 Windows 产物长什么样

在 Windows 上跑 wails buildbuild/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.jsoninfo 段:

{
  "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.jsoninfo 段配置。要深度定制就改 project.nsi

图标从 build/appicon.png 自动转,改了不生效多半是旧的 icon.ico 还在。

下一章换到 macOS,那边的重点从「运行时依赖」变成了「签名与公证」——不签名的 app 在别人机器上根本打不开。