1. 背景

我在做一个 WinUI3 项目(AotNativeWinui3),最初是按“全自包含 + AOT + 裁剪”去发版。
直觉上应该很小,但实际发布目录文件很多,体积也不理想。

后面我对照做了几轮实测,最终得到一套更稳定、可解释的参数组合。


注意事项(一定要看)

  1. WindowsAppSDKSelfContained=false 前提是目标机有环境。
  2. 裁剪后必须做核心路径回归(尤其反射/动态访问相关)。
  3. AOT 发布建议至少保留一套“带符号”的构建命令,方便问题定位。
  4. F5 开发调试正常可用;验证发布产物建议“先 publish 再附加调试”。
  5. DisableRuntimeMarshalling 不建议为“追体积”而开启;它主要影响互操作行为和稳定性,不是体积优化主开关。

2. 一句话先说结论

  • AOT 的优势确实很大,特别是启动速度。
  • 但“体积大头”常常不是 IL,而是运行时和本机依赖(WinAppSDK、WebView2、ONNX、DirectML 等)。
  • 如果目标机器已经有运行环境(WebView2 + Windows App Runtime),可以用“小包 AOT”策略:
    • SelfContained=true
    • PublishAot=true
    • PublishTrimmed=true
    • WindowsAppSDKSelfContained=false
  • 我这边实测 Release 可做到约 6.2 MB(6 个文件)

3. 我踩过的关键点

3.1 AOT 不是万能“压缩器”

AOT 的核心是“预编译 IL 到本机代码”,它主要改善启动和运行时行为。
它不会自动把所有外部本机依赖都消掉。

3.2 裁剪只裁托管可分析代码

PublishTrimmed=true 主要针对托管代码路径。
对于 WinUI3 / WinAppSDK / WebView2 这类本机依赖,裁剪帮助有限。

3.3 体积真正分水岭:是否自带运行时

WindowsAppSDKSelfContained 这个参数,影响非常大。

  • true:更独立,但目录会明显变大。
  • false:目录变小,但要求目标机有对应运行时。

4. 参数说明

4.1 PublishAot

  • 含义:启用 Native AOT。
  • 价值:启动更快、内存曲线更稳定,通常也能减少主程序体积。
  • 风险:对动态代码、反射场景更敏感。

4.2 SelfContained

  • 含义:是否把 .NET 运行时打进产物。
  • 在 AOT 场景里通常需要 true

4.3 PublishTrimmed

  • 含义:开启裁剪。
  • 价值:减少托管代码体积。
  • 注意:可能误裁,需要关注 trim 警告并做回归验证。

4.4 PublishSingleFile

  • 含义:尝试单文件发布。
  • WinUI3 场景通常不作为首选,容易引入兼容性和排查成本。

4.5 WindowsAppSDKSelfContained

  • 含义:是否把 WinAppSDK runtime 一并放进发布目录。
  • 这是 WinUI3 项目里影响体积最明显的参数之一。

4.6 DebugType / DebugSymbols

  • 含义:控制 PDB。
  • 发版可关,调试可开。
  • 开启后可调试,但 PDB 体积会变大(这是正常现象)。

4.7 DisableRuntimeMarshalling

  • 含义:关闭运行时默认封送(Runtime Marshalling)。
  • 背景:.NET 在托管与非托管边界调用时,会做字符串、数组、结构体等类型的自动封送;关闭后,很多自动行为会被禁用,需要你显式处理互操作细节。
  • 价值:在高频互操作场景下,可能降低封送开销、提升可预测性,也更贴近 AOT 的“显式控制”思路。
  • 风险:对现有 P/Invoke / COM 互操作兼容性影响较大,迁移成本高,容易引入隐蔽问题。
  • 实战建议:
    • 像本文这个 WinUI3 + WebView2 工程,优先保持 false,先追求稳定性。
    • 只有在你明确识别到互操作成为瓶颈、并且具备完整互操作测试覆盖时,再评估切到 true

5. 我们最终采用的发布策略

5.1 策略目标

  • 保留 AOT 性能优势;
  • 把发布目录做小;
  • 用安装环境兜底运行时。

5.2 对应配置(SmallAotPublish)

<PropertyGroup Condition="'$(SmallAotPublish)' == 'true'">
  <SelfContained>true</SelfContained>
  <PublishAot>true</PublishAot>
  <PublishTrimmed>true</PublishTrimmed>
  <PublishSingleFile>false</PublishSingleFile>
  <DisableRuntimeMarshalling>false</DisableRuntimeMarshalling>
  <WindowsAppSDKSelfContained>false</WindowsAppSDKSelfContained>
</PropertyGroup>

5.3 发布命令

dotnet publish D:\Codes\NativeAOT\NativeAOT\AotNativeWinui3\AotNativeWinui3.csproj -c Release -r win-x64 -p:SmallAotPublish=true

6. 实测数据

6.1 小包 AOT(Release)

  • 命令:-p:SmallAotPublish=true
  • 结果:Files=6, TotalMB=6.2

典型文件:

  • AotNativeWinui3.exe
  • Microsoft.Web.WebView2.Core.dll
  • Microsoft.WindowsAppRuntime.Bootstrap.dll
  • WebView2Loader.dll
  • AotNativeWinui3.pri

7. Debug 和 Release 到底差在哪

即便你不显式写 DebugType/DebugSymbols-c Debug 和 -c Release 依然不同:

  1. 优化级别不同(Release 更偏性能与体积)
  2. 条件编译常量不同(如 DEBUG
  3. 调试体验不同(Debug 更友好)
  4. AOT 下最终可观测体积和行为仍建议以 Release 为准

9. 最终推荐命令清单

9.1 发版小包(默认)

dotnet publish D:\Codes\NativeAOT\NativeAOT\AotNativeWinui3\AotNativeWinui3.csproj -c Release -r win-x64 -p:SmallAotPublish=true

9.2 发版小包(带 PDB)

dotnet publish D:\Codes\NativeAOT\NativeAOT\AotNativeWinui3\AotNativeWinui3.csproj -c Release -r win-x64 -p:SmallAotPublish=true -p:DebugType=portable -p:DebugSymbols=true

9.3 开发验证(Debug)

dotnet publish D:\Codes\NativeAOT\NativeAOT\AotNativeWinui3\AotNativeWinui3.csproj -c Debug -r win-x64 -p:SmallAotPublish=true

10. 结语

这次实践让我更确定一件事:
WinUI3 + AOT 的优化,不是“开一个参数就结束”,而是一个组合策略:

  • AOT 决定性能基线;
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐