名称:dotnet-aot-wasm 描述:“将 .NET 编译到 WebAssembly AOT。Blazor/Uno WASM,大小与速度权衡,懒加载,Brotli。” 用户可调用:false
dotnet-aot-wasm
Blazor WASM 和 Uno WASM 应用程序的 WebAssembly AOT 编译:编译流水线、下载大小与运行时速度权衡、修剪交互、懒加载程序集以及用于下载优化的 Brotli 预压缩。
版本假设: .NET 8.0+ 基准。Blazor WASM AOT 在 .NET 6 中推出,并通过 .NET 8-10 进行改进。Uno WASM 使用类似的编译流水线,具有 Uno 特定工具。
重要权衡: 修剪和 AOT 对 WASM 工件大小有 相反影响。修剪通过移除未使用代码减少下载大小。AOT 增加 工件大小(原生 WASM 代码比 IL 大)但 改善 运行时执行速度。结合使用两者以达到最佳平衡。
范围
- 下载大小与运行时速度权衡分析
- Blazor WASM AOT(RunAOTCompilation、选择性 AOT)
- Uno WASM AOT(.NET 8+ 标准工作负载)
- 懒加载程序集以减少大小
- Brotli 预压缩用于下载优化
- WASM 大小优化检查清单
范围外
- 服务器端 .NET 的本地 AOT – 参见 [技能:dotnet-native-aot]
- AOT 优先设计模式 – 参见 [技能:dotnet-aot-architecture]
- 修剪安全库编写 – 参见 [技能:dotnet-trimming]
- MAUI 特定 AOT – 参见 [技能:dotnet-maui-aot]
- Blazor 托管模型和渲染模式 – 参见 [技能:dotnet-blazor-patterns]
- Blazor 组件生命周期和 JS 互操作 – 参见 [技能:dotnet-blazor-components]
- Uno Platform 架构 – 参见 [技能:dotnet-uno-platform]
交叉参考: [技能:dotnet-native-aot] 用于通用 AOT 流水线,[技能:dotnet-trimming] 用于修剪注释,[技能:dotnet-aot-architecture] 用于 AOT 安全设计模式,[技能:dotnet-serialization] 用于 AOT 安全序列化,[技能:dotnet-csharp-source-generators] 用于源生成作为 AOT 启用器,[技能:dotnet-blazor-patterns] 用于 Blazor 架构(软),[技能:dotnet-uno-platform] 用于 Uno Platform 模式(软)。
下载大小与运行时速度
理解大小/速度权衡对 WASM AOT 决策至关重要:
| 编译模式 | 下载大小 | 运行时速度 | 启动时间 |
|---|---|---|---|
| IL 解释器(无 AOT) | 最小 | 最慢 | 最快启动 |
| AOT(所有程序集) | 最大 | 最快 | 较慢启动 |
| AOT(选择性)+ 修剪 | 平衡 | 良好 | 中等 |
| 仅修剪(无 AOT) | 小 | 中等(JIT 解释) | 快 |
关键洞察: 修剪通过移除未使用 IL 减少大小。AOT 增加 总工件大小,因为编译的原生 WASM 代码比等效 IL 字节码大。然而,AOT 编译代码执行显著更快,因为它跳过了运行时的 IL 解释。
何时使用 WASM AOT
- CPU 密集型工作负载: 图像处理、复杂计算、数据转换
- 可预测性能: 一致执行速度,无 JIT 暂停
- 热点路径: 仅 AOT 编译性能关键程序集(选择性 AOT)
何时跳过 WASM AOT
- 带宽受限用户: AOT 显著增加下载大小
- 简单 CRUD 应用: IL 解释对 UI 交互和 API 调用足够快
- 快速迭代: AOT 编译增加显著发布时间
Blazor WASM AOT
启用 AOT
<!-- Blazor WASM .csproj -->
<PropertyGroup>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
# 使用 AOT 发布(必需 -- AOT 仅在发布期间应用)
dotnet publish -c Release
注意:RunAOTCompilation 是 Blazor WASM 属性(非 PublishAot,后者用于服务器端本地 AOT)。AOT 编译仅在 dotnet publish 期间发生,不在 dotnet run 或 dotnet build 期间。
通过懒加载进行选择性 AOT
Blazor WASM AOT 编译所有非懒加载程序集。要控制哪些程序集 AOT 编译,将非关键程序集标记为懒加载 – 它们将使用 IL 解释代替:
<PropertyGroup>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
<ItemGroup>
<!-- 这些程序集不 AOT 编译(通过 IL 解释器按需加载) -->
<BlazorWebAssemblyLazyLoad Include="MyApp.Reporting.wasm" />
<BlazorWebAssemblyLazyLoad Include="MyApp.Admin.wasm" />
<!-- 所有其他程序集(MyApp.Core、MyApp.Calculations 等)AOT 编译 -->
</ItemGroup>
修剪 + AOT 结合
为最佳平衡,同时使用修剪和 AOT:
<PropertyGroup>
<!-- 修剪减少未使用代码(更小下载) -->
<PublishTrimmed>true</PublishTrimmed>
<!-- AOT 编译剩余代码到原生 WASM(更快执行) -->
<RunAOTCompilation>true</RunAOTCompilation>
<!-- 开发期间详细警告 -->
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
发布流水线运行:先修剪未使用 IL,然后将剩余程序集 AOT 编译到原生 WASM。这产生一个比仅修剪大但比无修剪 AOT 小的工件,具有最佳运行时性能。
Uno WASM AOT
Uno Platform 5+ 与 .NET 8+ 使用标准 .NET WASM 工作负载,因此 AOT 配置与 Blazor WASM 相同。
启用 AOT(Uno 5+ / .NET 8+)
<!-- Uno WASM 头 .csproj -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-browserwasm'">
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
旧版 Uno 使用 Uno.Wasm.Bootstrap 具有单独的 WasmShellMonoRuntimeExecutionMode 属性,带有 Interpreter、InterpreterAndAOT 和 FullAOT 模式。在 .NET 8+,使用 RunAOTCompilation 代替。
Uno WASM 中的修剪
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
参见 [技能:dotnet-uno-platform] 用于 Uno Platform 架构模式。
懒加载程序集
懒加载推迟下载程序集直到需要,减少初始下载大小。这在结合 AOT(增加每程序集大小)时特别有效。
Blazor WASM 懒加载
<!-- 在 .csproj 中标记程序集为懒加载 -->
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="MyApp.Reporting.wasm" />
<BlazorWebAssemblyLazyLoad Include="MyApp.Admin.wasm" />
<BlazorWebAssemblyLazyLoad Include="ChartLibrary.wasm" />
</ItemGroup>
// 在组件或路由器中按需加载程序集
@inject LazyAssemblyLoader LazyLoader
@code {
private List<Assembly> _lazyLoadedAssemblies = new();
private async Task LoadReportingModule()
{
var assemblies = await LazyLoader.LoadAssembliesAsync(new[]
{
"MyApp.Reporting.wasm"
});
_lazyLoadedAssemblies.AddRange(assemblies);
}
}
基于路由器的懒加载
<!-- App.razor -->
@inject LazyAssemblyLoader LazyLoader
<Router AppAssembly="typeof(App).Assembly"
AdditionalAssemblies="@_lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync">
<Navigating>
<div class="loading">加载模块...</div>
</Navigating>
</Router>
@code {
private List<Assembly> _lazyLoadedAssemblies = new();
private async Task OnNavigateAsync(NavigationContext context)
{
if (context.Path.StartsWith("admin"))
{
var assemblies = await LazyLoader.LoadAssembliesAsync(new[]
{
"MyApp.Admin.wasm"
});
_lazyLoadedAssemblies.AddRange(assemblies);
}
else if (context.Path.StartsWith("reports"))
{
var assemblies = await LazyLoader.LoadAssembliesAsync(new[]
{
"MyApp.Reporting.wasm"
});
_lazyLoadedAssemblies.AddRange(assemblies);
}
}
}
懒加载策略
| 策略 | 初始加载 | 功能加载 | 最适合 |
|---|---|---|---|
| 无懒加载 | 全部一次性 | 即时 | 小应用(<5 MB 总计) |
| 基于路由的懒加载 | 仅核心 | 导航时 | 多模块应用 |
| 基于功能的懒加载 | 仅核心 | 按需 | 具有可选功能的应用 |
Brotli 预压缩
Brotli 预压缩通过 60-80% 减少 WASM 下载大小。Blazor WASM 在发布期间自动生成 Brotli 压缩文件。
工作原理
在 dotnet publish 期间,Blazor WASM 为 _framework/ 中所有静态文件生成 .br(Brotli)和 .gz(gzip)压缩版本。当浏览器支持时,Web 服务器提供预压缩文件。
# 发布后,检查压缩大小
ls -la bin/Release/net8.0/publish/wwwroot/_framework/
# 您会看到:
# MyApp.wasm (原始)
# MyApp.wasm.br (Brotli 压缩,约 60-80% 更小)
# MyApp.wasm.gz (gzip 压缩,约 50-70% 更小)
服务器配置
Web 服务器必须配置为提供预压缩文件。大多数 Blazor 托管设置自动处理此。
ASP.NET Core 托管:
// 在托管 Blazor WASM 的服务器项目中
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
// Blazor 框架文件自动以压缩头提供
Nginx:
location /_framework/ {
# 当可用时,提供 Brotli 压缩文件
gzip_static on;
brotli_static on;
# 设置正确 MIME 类型
types {
application/wasm wasm;
}
# 积极缓存(文件内容哈希)
add_header Cache-Control "public, max-age=31536000, immutable";
}
Azure Static Web Apps / GitHub Pages:
当 Accept-Encoding: br 头存在时,预压缩 .br 文件自动提供。
压缩影响
| 内容 | 原始 | Brotli (.br) | 减少 |
|---|---|---|---|
| .NET WASM 运行时 | ~2.5 MB | ~0.8 MB | ~68% |
| 应用程序集(IL) | 可变 | ~70% 更小 | ~70% |
| 应用程序集(AOT) | 可变 | ~65% 更小 | ~65% |
| JavaScript 胶水代码 | ~100 KB | ~25 KB | ~75% |
禁用压缩(很少需要)
<!-- 禁用 Brotli 预压缩 -->
<PropertyGroup>
<BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>
WASM 大小优化检查清单
- 启用修剪 – 在 AOT 编译前移除未使用 IL
- 使用懒加载 – 推迟非关键程序集
- 启用 Brotli 预压缩 – 传输大小减少 60-80%(默认开启)
- 使用选择性 AOT – 仅 AOT 编译性能关键程序集
- 如果不需要文化特定格式化,启用不变全球化:
<PropertyGroup> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup> - 移除未使用框架功能:
<PropertyGroup> <!-- 禁用您不使用的功能 --> <EventSourceSupport>false</EventSourceSupport> <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport> </PropertyGroup> - 验证压缩被提供 – 检查浏览器 DevTools 网络标签中的
content-encoding: br
代理常见错误
- 不要混淆
RunAOTCompilation与PublishAot。 Blazor WASM 使用RunAOTCompilation用于 WASM AOT。PublishAot用于服务器端本地 AOT,产生不同类型二进制文件。 - 不要假设 AOT 减少 WASM 下载大小。 AOT 增加 工件大小,因为原生 WASM 代码比 IL 字节码大。使用修剪减少大小,AOT 改善运行时速度。
- 不要忘记在测试 AOT 时发布。 WASM AOT 仅在
dotnet publish期间运行,不在dotnet run。调试构建总是使用 IL 解释。 - 不要懒加载启动时需要的程序集。 仅懒加载初始导航后访问的功能程序集。加载懒程序集触发网络请求。
- 不要跳过 Brotli 压缩验证。 确保您的 Web 服务器提供
.br文件。无压缩时,WASM 下载比必要大 3-5 倍。检查浏览器 DevTools 中的content-encoding: br头。 - 当下载大小重要时,不要 AOT 编译所有程序集。 使用
BlazorWebAssemblyLazyLoad推迟非关键程序集 – 懒加载程序集使用 IL 解释代替 AOT。