上一篇我写那个 Wails v3 本地开发者工具箱时,重点放在了打包体积上。
Windows 下出来的 exe 只有 8.70MB,这个数字确实很显眼。
结果文章发出去后,评论区里不少人都在问另一件事:
包是小了,那运行起来到底吃多少内存?
这个问题我觉得很有必要测一下。
因为 8.70MB 只能说明分发体积小,不能直接说明运行时也更省。
所以这篇不聊源码,也不聊界面,我就只做一件事:把 Wails 和 Electron 的运行内存拉出来实测一遍。
先把结果放前面:
这次在我这台 Windows 机器上,用同一套多功能工具箱去测,Wails 的打包体积还是明显更小,但运行内存并没有比 Electron 更低。
而且这次测出来,Wails 还高一点。
这个结果我一开始也没预设到,但实测就是这样。
先说清楚:测试的标准都是一样的工具箱
上一篇那个 Wails 项目,本身是个多功能工具箱。
所以我用electron也写了个一样的的工具箱
让他们在同一个标准下进行测试

测试环境
先把环境放出来,免得别人把这组数据当成通用结论。
| 项目 | 版本 / 信息 |
|---|---|
| 系统 | Windows 11 家庭中文版 |
| CPU | AMD Ryzen 7 8745H |
| Node | v24.14.1 |
| npm | 11.11.0 |
| Go | 1.24.0 |
| Wails | v3.0.0-alpha.78 |
| Electron | 39.2.7 |
下面这些结果,只代表我当前这台机器、这组工具箱和这套打包配置。
我怎么测的
这次我没有手动开任务管理器看一眼就下结论,而是直接写脚本去跑。
命令是:
powershell -ExecutionPolicy Bypass -File .\measure-memory.ps1
脚本做的事情不复杂:
- 应用启动后先等 6 秒
- 连续测 3 次
- Wails 不只看主进程,也把它拉起来的
msedgewebview2.exe一起算进去 - Electron 统计应用目录下的所有相关进程
- 同时输出
Working Set和Private Bytes
测试脚本如下
param(
[int]$StartupDelaySeconds = 6,
[int]$RepeatCount = 3
)
function Format-MB {
param([Parameter(Mandatory = $true)][double]$Bytes)
return "{0:N2} MB" -f ($Bytes / 1MB)
}
function Get-ProcessTree {
param(
[Parameter(Mandatory = $true)][int]$RootProcessId
)
$all = Get-CimInstance Win32_Process
$queue = [System.Collections.Generic.Queue[int]]::new()
$seen = [System.Collections.Generic.HashSet[int]]::new()
$result = [System.Collections.Generic.List[object]]::new()
$queue.Enqueue($RootProcessId)
while ($queue.Count -gt 0) {
$currentId = $queue.Dequeue()
if (-not $seen.Add($currentId)) {
continue
}
$proc = $all | Where-Object { $_.ProcessId -eq $currentId } | Select-Object -First 1
if ($null -eq $proc) {
continue
}
$result.Add($proc)
$children = $all | Where-Object { $_.ParentProcessId -eq $currentId }
foreach ($child in $children) {
$queue.Enqueue([int]$child.ProcessId)
}
}
return $result
}
function Stop-ProcessTree {
param(
[Parameter(Mandatory = $true)][System.Collections.IEnumerable]$Processes
)
$processList = @($Processes | Sort-Object ProcessId -Descending)
foreach ($proc in $processList) {
if (Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue) {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
}
}
}
function Measure-AppMemory {
param(
[Parameter(Mandatory = $true)][string]$Name,
[Parameter(Mandatory = $true)][string]$ExePath,
[string]$ProcessPathPrefix
)
if (-not (Test-Path -LiteralPath $ExePath -PathType Leaf)) {
throw "Executable not found: $ExePath"
}
$root = Start-Process -FilePath $ExePath -PassThru
Start-Sleep -Seconds $StartupDelaySeconds
try {
$tree = @(Get-ProcessTree -RootProcessId $root.Id)
if ($ProcessPathPrefix) {
$tree = @(
$tree | Where-Object {
$_.ExecutablePath -and (
$_.ExecutablePath.StartsWith($ProcessPathPrefix, [System.StringComparison]::OrdinalIgnoreCase) -or
$_.ExecutablePath -eq $ExePath
)
}
)
}
$workingSet = ($tree | Measure-Object -Property WorkingSetSize -Sum).Sum
$privateBytes = ($tree | Measure-Object -Property PrivatePageCount -Sum).Sum
[pscustomobject]@{
App = $Name
ProcessCount = $tree.Count
WorkingSetMB = [math]::Round($workingSet / 1MB, 2)
PrivateMB = [math]::Round($privateBytes / 1MB, 2)
WorkingSet = Format-MB $workingSet
PrivateBytes = Format-MB $privateBytes
Processes = ($tree | ForEach-Object { $_.Name } | Sort-Object | Get-Unique) -join ", "
}
}
finally {
$liveTree = Get-ProcessTree -RootProcessId $root.Id
Stop-ProcessTree -Processes $liveTree
}
}
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$wailsExe = Join-Path $root "wails-json-tool\json-tool-wails\bin\json-tool-wails.exe"
$electronDir = Join-Path $root "electron-json-tool\release\JSON Tool Electron-win32-x64"
$electronExe = Join-Path $electronDir "JSON Tool Electron.exe"
$measurements = @()
for ($i = 1; $i -le $RepeatCount; $i++) {
$measurements += Measure-AppMemory -Name "Wails v3" -ExePath $wailsExe | Select-Object *, @{Name = "Run"; Expression = { $i } }
$measurements += Measure-AppMemory -Name "Electron" -ExePath $electronExe -ProcessPathPrefix $electronDir | Select-Object *, @{Name = "Run"; Expression = { $i } }
}
$measurements |
Sort-Object App, Run |
Format-Table App, Run, ProcessCount, WorkingSet, PrivateBytes -AutoSize
""
$summary = $measurements |
Group-Object App |
ForEach-Object {
$workingSetAverage = ($_.Group | Measure-Object -Property WorkingSetMB -Average).Average
$privateAverage = ($_.Group | Measure-Object -Property PrivateMB -Average).Average
$processCountAverage = ($_.Group | Measure-Object -Property ProcessCount -Average).Average
[pscustomobject]@{
App = $_.Name
AvgProcessCount = [math]::Round($processCountAverage, 2)
AvgWorkingSet = "{0:N2} MB" -f $workingSetAverage
AvgPrivateBytes = "{0:N2} MB" -f $privateAverage
Processes = ($_.Group[0].Processes)
}
}
$summary | Format-Table App, AvgProcessCount, AvgWorkingSet, AvgPrivateBytes, Processes -AutoSize
这里要特意说一句。
如果只看主进程,结果很容易偏。
因为 Wails 在 Windows 下走的是 WebView2,启动后会带起一串 msedgewebview2.exe;Electron 本身也不是单进程程序。
所以这次我尽量按“这个应用真正启动后占了多少系统资源”来算,而不是只看一个 exe 漂不漂亮。
先看截图
这是我直接跑脚本后整理出来的结果:

实测结果
把 3 次结果和平均值整理成表格,大概是这样:
| 指标 | Wails 工具箱 | Electron 工具箱 |
|---|---|---|
| 进程数 | 7 | 4 |
| 平均 Working Set | 403.96 MB | 330.91 MB |
| 平均 Private Bytes | 219.77 MB | 203.72 MB |
| 打包产物 | 8.70 MB exe | 341.93 MB 解包目录 |
单次结果波动也不算大:
| 应用 | 第 1 次 | 第 2 次 | 第 3 次 |
|---|---|---|---|
| Wails Working Set | 407.45 MB | 402.92 MB | 401.52 MB |
| Electron Working Set | 330.88 MB | 330.82 MB | 331.03 MB |
也就是说,至少在这轮测试里,结论挺直接:
Wails 的包体积更小,但这组工具箱的空载内存并没有更低。
这个结果为什么会这样
我觉得主要是两件事。
第一,安装包小,不等于运行时一定小
这个其实是最容易被顺手带过去的地方。
Wails 的优势之一,确实是分发出来的东西更小。
因为它没有像 Electron 那样把完整的 Chromium 一起打进包里,而是更多依赖系统已有的 WebView 运行时。
所以从安装包和分发角度看,Wails 还是轻的。
但这个“轻”,不能自动翻译成“运行内存也一定更低”。
第二,Wails 不是没有浏览器那层成本
有些人会下意识觉得,Wails 是 Go,Electron 是 Chromium,所以 Wails 运行起来应该天然更省。
这次实测至少说明,事情没这么简单。
Wails 在 Windows 下还是走 WebView2,也就是系统 WebView。
换句话说,它不是完全没有浏览器渲染层,只是这套运行时不跟着应用一起打包而已。
所以你会看到:
- 包体积小很多
- 运行起来依然会有 WebView2 相关进程
这两件事其实并不矛盾。
那是不是能说 Wails 比 Electron 更吃内存
我觉得也不能这么写死。
更稳的说法应该是:
在我这台 Windows 机器、这组同功能工具箱、当前版本下,Wails 的打包体积明显更小,但空载内存并没有比 Electron 更低。
这里面有几个变量都可能影响结果:
- WebView2 版本
- Electron 版本
- 前端页面复杂度
- 是否有更多窗口
- 是否有更多本地能力调用
- 不同系统环境下的进程表现
所以这篇更像是在纠正一个很容易被带偏的印象:
8.70MB说的是包体积,不是运行时成本。
那 Wails 还值不值得用
我自己的答案还是:值。
只是卖点要说准。
如果你更在意这些:
- 安装包别太大
- 不想把 Chromium 一起塞进产物
- 本身就是 Go 开发者
- 做的是轻量工具、内部工具、本地工具
那 Wails 还是很有吸引力。
但如果你原本的预期是:
换成 Wails 以后,包更小,内存也一定更小
那至少这次这组工具箱实测没支持这个结论。
最后
这轮测试对我来说,反而把 Wails 的定位看得更清楚了一点。
它的优势更像是:
- 分发轻
- Go 接起来顺
- 做小工具很舒服
而不是简单一句“它比 Electron 更小”。