关于 dotnet-uno-targets
为 Uno Platform 应用开发者提供按目标平台划分的部署指南,涵盖 Web/WASM、移动端、桌面端和嵌入式环境。内容包括项目配置、调试、打包分发及平台特有注意事项。
主要功能
- 01按目标平台介绍项目配置
- 02说明调试工作流与工具
- 03介绍打包和分发方式
- 04涵盖 AOT 与裁剪影响
- 05整理平台特有的限制与差异
适用场景
- 01为 Uno 应用配置特定部署目标
- 02排查各平台的调试与运行问题
- 03准备应用打包和分发
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-uno-targets安装后在 Claude Code 中输入/dotnet-uno-targets确认已加载。
详细介绍
dotnet-uno-targets
Uno Platform 应用的各目标部署指南:Web/WASM、iOS、Android、macOS (Catalyst)、Windows、Linux (Skia/GTK) 和 Embedded (Skia/Framebuffer)。每个目标章节涵盖项目设置、调试工作流、打包/分发、平台特有的注意事项、AOT/裁剪影响,以及与其他目标的行为差异。
范围边界: 此技能负责各目标的部署、调试、打包和平台特有的注意事项。Uno Platform 核心开发(Extensions、MVUX、Toolkit、主题)由 [skill:dotnet-uno-platform] 负责。实时文档的 MCP 集成由 [skill:dotnet-uno-mcp] 负责。
范围之外: Uno Platform 测试(WASM 的 Playwright、平台 Appium 测试)——请参阅 [skill:dotnet-uno-testing]。通用 AOT/裁剪模式——请参阅 [skill:dotnet-aot-wasm]。UI 框架选择——请参阅 [skill:dotnet-ui-chooser]。
交叉引用:[skill:dotnet-uno-platform](核心开发)、[skill:dotnet-uno-mcp](MCP 集成)、[skill:dotnet-uno-testing](测试)、[skill:dotnet-aot-wasm](通用 WASM AOT 模式)、[skill:dotnet-ui-chooser](框架选择)。
目标平台概览
| 目标 | TFM | 工具链 | 打包方式 | 主要限制 |
|---|---|---|---|---|
| Web/WASM | net8.0-browserwasm | 浏览器开发者工具 | 静态托管 / Azure SWA | 无法访问文件系统,建议使用 AOT,多线程能力有限 |
| iOS | net8.0-ios | Xcode / VS Code / Rider | App Store / TestFlight | 配置描述文件、entitlements、不支持 JIT |
| Android | net8.0-android | Android SDK / Emulator | Play Store / APK 旁加载 | SDK 版本目标设置、权限 |
| macOS (Catalyst) | net8.0-maccatalyst | Xcode | Mac App Store / 公证 | 沙盒限制、entitlements |
| Windows | net8.0-windows10.0.19041 | Visual Studio | MSIX / Windows Store | WinAppSDK 版本对齐 |
| Linux | net8.0-desktop | Skia/GTK 主机 | AppImage / Flatpak / Snap | GTK 依赖、Skia 渲染 |
| Embedded | net8.0-desktop | Skia/Framebuffer | 直接部署 | 无窗口系统、无头渲染 |
TFM 说明: 以编程方式检测平台目标时,使用与版本无关的 glob(net*-ios、net*-android),以避免较旧或较新的 TFM 导致误判。
Web/WASM
项目设置
# Run the WASM target
dotnet run -f net8.0-browserwasm --project MyApp/MyApp.csproj
WASM 目标会在浏览器中渲染 XAML 控件。渲染器取决于项目配置:Skia(canvas/WebGL)或原生 HTML 映射。应用通过 JavaScript 引导程序(uno-bootstrap.js)加载,该程序会初始化 .NET WASM 运行时。
调试
- 浏览器开发者工具: 在 Chrome/Edge 中按 F12 检查 DOM、网络和控制台输出
- .NET 调试: Visual Studio 和 VS Code 支持通过浏览器 CDP 调试 .NET WASM 运行时
- Uno DevServer:
dotnet run会启动支持实时重新加载的开发服务器 - 控制台日志:
ILogger输出会显示在浏览器控制台中
打包/分发
# Publish for production
dotnet publish -f net8.0-browserwasm -c Release --output ./publish
# Output is a static site (HTML/JS/WASM)
# Deploy to: Azure Static Web Apps, GitHub Pages, Netlify, any static host
发布的输出是一个自包含的静态网站,无需服务器端运行时。
平台特有的注意事项
- 无法访问文件系统: 通过 JS 互操作或 Uno.Storage 使用浏览器存储 API(IndexedDB、localStorage)
- 线程限制: Web Workers 仅提供有限的多线程能力;
Task.Run在 WASM 上可能不会并行执行 - CORS 限制: 来自 WASM 的 HTTP 请求受浏览器 CORS 策略约束
- 初始加载时间: 应用可用前,必须下载 .NET WASM 运行时和程序集。使用程序集裁剪来减小下载体积。AOT 可提升运行时执行速度,但会增加生成产物的体积
- 深层链接: 在 Uno Navigation Extensions 中配置 URL 路由,以支持通过浏览器地址栏导航
AOT/修剪
修剪通过移除未使用的代码来减小下载体积。AOT 会将 IL 预编译为 WebAssembly,从而提升运行时执行速度,但会增大产物体积。两者结合使用,并衡量它们对应用的影响。
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-browserwasm'">
<WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
**修剪对 WASM 至关重要。**未经修剪的应用可能超过 30MB。经过修剪后,典型应用的大小为 5-15MB。AOT 会增大产物体积,但能消除运行时解释器开销——请在目标环境下分别评估下载时间和执行速度。
有关 Uno 特有的 AOT 注意事项(链接器描述符、Uno 源生成器),请参阅 AOT 部分。有关通用的 WASM AOT 模式,请参阅 [skill:dotnet-aot-wasm]。
行为差异
- **导航:**基于 URL 的深层链接可原生使用;路由映射应定义 URL 模式
- **身份验证:**OAuth 流程使用浏览器重定向(弹出窗口或重定向);没有可用的原生浏览器。令牌存储使用浏览器安全存储
- **调试:**可通过 CDP 使用完整的 .NET 调试器;断点可在 Visual Studio/VS Code 中使用
- **文件选择器:**使用浏览器文件输入 API;
FileOpenPicker映射到<input type="file">
iOS
项目设置
# Build for iOS simulator
dotnet build -f net8.0-ios
# Run on simulator
dotnet run -f net8.0-ios
需要在 macOS 上安装 Xcode。渲染器(Skia 或原生)取决于项目配置和 Uno 版本。
调试
- **Visual Studio(配对到 Mac)/ VS Code + C# Dev Kit / Rider:**附加到 iOS 模拟器或设备
- **Xcode Instruments:**分析 CPU、内存和能耗使用情况
- **热重载:**通过
DOTNET_MODIFIABLE_ASSEMBLIES=debug支持
打包/分发
# Publish for App Store
dotnet publish -f net8.0-ios -c Release \
/p:CodesignKey="Apple Distribution: MyCompany" \
/p:CodesignProvision="MyApp Distribution Profile"
分发渠道:App Store(需要 Apple Developer 账户)、TestFlight(Beta 测试)、Ad Hoc(企业分发)。
**必需:**配置文件、签名证书,以及用于各项功能的权利文件(推送通知、HealthKit 等)。
平台注意事项
- **无 JIT 编译:**iOS 禁止 JIT。所有代码都必须使用 AOT 编译或解释执行。.NET 运行时默认使用 Mono 解释器
- **配置文件:**必须与 bundle ID、team ID 和权利相匹配。配置文件过期会导致难以诊断的构建失败
- **后台执行:**iOS 限制后台处理。请使用
BGTaskScheduler执行后台工作 - **应用传输安全(ATS):**默认要求 HTTPS;HTTP 需要在
Info.plist中添加NSAppTransportSecurity例外 - **内存压力:**iOS 会积极终止后台应用。请处理
MemoryWarning事件
AOT/修剪
iOS 默认要求使用 AOT(不支持 JIT)。.NET 运行时会编译为原生 ARM64 代码。
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
**注意:**使用大量反射的代码在 iOS 上会静默失败。开发期间就启用修剪进行测试,不要只在发布构建时测试。
行为差异
- **导航:**使用 Uno Navigation 时,基于手势的返回导航(从左边缘向右滑动)会自动启用
- **身份验证:**OAuth 使用
ASWebAuthenticationSession;生物识别身份验证通过LocalAuthentication框架实现 - **调试:**模拟器速度快;设备调试需要 USB 连接和配置文件。支持通过热重载进行远程调试
Android
项目设置
# Build for Android
dotnet build -f net8.0-android
# Deploy to connected device or emulator
dotnet run -f net8.0-android
需要 Android SDK(通过 dotnet workload install android 或 Android Studio 安装)。
调试
- **Android 模拟器:**使用 Android Studio 的模拟器或命令行
emulator - **ADB(Android 调试桥):**使用
adb logcat查看运行时日志,使用adb devices验证连接 - **Visual Studio / VS Code:**可在模拟器或设备上使用完整的断点调试
打包/分发
# Publish signed APK/AAB (use env vars or CI secrets for passwords — never hardcode)
dotnet publish -f net8.0-android -c Release \
/p:AndroidKeyStore=true \
/p:AndroidSigningKeyStore=mykey.keystore \
/p:AndroidSigningStorePass="$ANDROID_KEYSTORE_PASS" \
/p:AndroidSigningKeyAlias=myalias \
/p:AndroidSigningKeyPass="$ANDROID_KEY_PASS"
Google Play 要求使用 Android App Bundle(AAB)格式。侧载使用 APK。
平台注意事项
- SDK 版本目标:
<SupportedOSPlatformVersion>控制最低 API 级别。为符合 Google Play 要求,目标 API 需为 34+ - 运行时权限: 危险权限(摄像头、位置、存储)需要在运行时请求。应在代码中检查并请求,而不只是添加到清单中
- Activity 生命周期: 默认情况下,Android 会在屏幕旋转时销毁 Activity。Uno 会处理此情况,但自定义原生代码必须考虑生命周期
- ProGuard/R8: 代码缩减可能会移除反射目标。对原生互操作类型使用
@Keep注解或链接器配置
AOT/修剪
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PublishTrimmed>true</PublishTrimmed>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
Android 上的 AOT 可以缩短启动时间。与 iOS 不同,未修剪的代码路径可回退到 JIT。
行为差异
- 导航: 硬件或软件返回按钮会触发 Uno 导航返回。
BackRequested事件会自动触发 - 身份验证: 使用
CustomTabs(Chrome)进行 OAuth;通过AndroidX.Biometric进行生物识别身份验证 - 调试: 模拟器比 iOS Simulator 慢,但支持更多 API 级别。使用硬件加速(HAXM/KVM)提升性能
macOS(Catalyst)
项目设置
# Build for macOS
dotnet build -f net8.0-maccatalyst
# Run on macOS
dotnet run -f net8.0-maccatalyst
使用 Mac Catalyst(将 iOS API 适配到 macOS)。需要在 macOS 上安装 Xcode。
调试
- Visual Studio(配对到 Mac)/ VS Code + C# Dev Kit / Rider: 原生 macOS 调试
- Xcode Instruments: 分析性能和内存
- Console.app: 查看应用进程的系统日志
打包/分发
# Publish for distribution
dotnet publish -f net8.0-maccatalyst -c Release \
/p:CodesignKey="Developer ID Application: MyCompany" \
/p:CodesignProvision="MyApp Mac Profile"
分发渠道:Mac App Store、经过公证的直接下载、企业部署。
必须进行公证: macOS Gatekeeper 要求在 App Store 之外分发的应用经过公证。使用 xcrun notarytool 提交。
平台注意事项
- 沙盒限制: Mac App Store 应用运行在沙盒中。访问文件需要相应的权利和用户授权
- Catalyst 限制: 并非所有 iOS API 都能适用于 macOS。请检查
SupportedOSPlatform属性 - 菜单栏: macOS 要求提供合适的菜单栏。Uno 提供默认菜单;可通过平台专用代码自定义
- 窗口管理: macOS 应用支持多个窗口。Uno 的单窗口模型可能需要调整,以支持多窗口场景
AOT/修剪
与 iOS 使用相同的配置(Catalyst 默认使用 AOT)。建议在分发版本中进行修剪。
行为差异
- 导航: 没有向后滑动手势;需使用工具栏上的返回按钮或键盘快捷键(Cmd+[)
- 身份验证: 使用
ASWebAuthenticationSession(与 iOS 共用);通过 Secure Enclave 支持 Touch ID - 调试: 作为原生 macOS 进程运行;标准 .NET 调试工具均可使用。没有模拟器——以原生应用形式运行
Windows
项目设置
# Build for Windows
dotnet build -f net8.0-windows10.0.19041
# Run on Windows
dotnet run -f net8.0-windows10.0.19041
Windows 目标可以使用 Skia 渲染器,也可以使用原生 WinAppSDK/WinUI 3 渲染。
调试
- Visual Studio: 提供完整调试功能,包括 XAML Hot Reload、Live Visual Tree、Live Property Explorer
- WinUI 诊断: 内置诊断浮层,可用于检查布局
打包/分发
# Package as MSIX
dotnet publish -f net8.0-windows10.0.19041 -c Release \
/p:PackageOutputPath=./packages
分发方式:Microsoft Store(MSIX)、旁加载(使用证书的 MSIX)、ClickOnce 或直接分发 EXE。
平台注意事项
- WinAppSDK 版本对齐: Windows TFM 版本必须与最低 Windows 版本匹配。
10.0.19041= Windows 10 2004+ - UAC 和提权: 应用无法自行提权。应针对标准用户权限进行设计
- Windows 专用 API:
Windows.Storage、Windows.NetworkingAPI 仅在 Windows 目标上可用。请使用条件编译或 Uno 抽象 - MSIX 签名: MSIX 包必须经过签名才能安装。分发时请使用代码签名证书
AOT/修剪
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
Windows 支持 JIT 和 AOT。AOT 会生成单个原生 EXE,启动速度更快。
行为差异
- **导航:**标准 Windows 导航(Alt+Left 返回,标题栏返回按钮)
- **身份验证:**使用系统浏览器或 WAM(Web Account Manager),通过 Microsoft 账户进行 SSO
- **调试:**调试体验最丰富,支持 Visual Studio Live Visual Tree 和 XAML Hot Reload
Linux(Skia/GTK)
项目设置
# Build for Linux desktop
dotnet build -f net8.0-desktop
# Run on Linux
dotnet run -f net8.0-desktop
Linux 目标使用 Skia 渲染器和 GTK 宿主窗口。所有 XAML 渲染均由 Skia 完成,GTK 仅负责窗口和输入处理。
调试
- **VS Code with C# Dev Kit:**在 Linux 上进行远程调试
- **JetBrains Rider:**完整支持 Linux 调试
- 控制台日志:
dotnet run会将日志输出到终端
打包/分发
# Publish self-contained for Linux
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-x64
# Package as AppImage, Flatpak, or Snap
分发方式:AppImage(便携,无需安装)、Flatpak(沙盒化)、Snap(Ubuntu Store)、DEB/RPM 包。
平台注意事项
- **GTK 依赖项:**应用在运行时需要 GTK3 库。请打包或说明依赖项:
libgtk-3-0、libskia* - **Skia 渲染:**所有渲染均基于 Skia。不使用原生 GTK 小部件。这可确保跨平台渲染的像素级精确,但意味着应用不会遵循宿主 GTK 主题
- **字体渲染:**确保目标系统上有可用字体。请将字体嵌入应用,或声明字体依赖项
- **显示缩放:**Skia 支持 HiDPI;请使用
GDK_SCALE=2环境变量进行测试
AOT/裁剪
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-desktop'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
Linux 上的 AOT 会生成原生二进制文件。自包含部署可避免依赖系统范围的 .NET 运行时。
行为差异
- **导航:**由键盘驱动(Alt+Left 返回)。不支持基于手势的导航
- **身份验证:**OAuth 流程使用系统浏览器。不支持平台专用的身份验证 API
- **调试:**标准 .NET 调试。不支持平台专用的调试器工具
嵌入式(Skia/Framebuffer)
项目设置
嵌入式目标使用 Skia 渲染器和 framebuffer 后端,可在无窗口系统的情况下进行无头或信息亭式渲染。
# Build for embedded (same TFM as desktop)
dotnet build -f net8.0-desktop -r linux-arm64
# Run directly on embedded device
dotnet run -f net8.0-desktop
嵌入式目标与 Linux 桌面共用 net8.0-desktop TFM。平台专用配置会选择 framebuffer 后端。
// Program.cs -- framebuffer host configuration
public static void Main(string[] args)
{
SkiaHostBuilder.Create()
.UseFrameBuffer() // Use framebuffer instead of GTK
.App(() => new App())
.Build()
.Run();
}
调试
- **SSH 远程调试:**使用 VS Code Remote 或
dotnet-trace进行远程性能分析 - **串行控制台:**将日志重定向到串行输出,以便进行无头调试
- **远程日志记录:**配置 Serilog 并使用网络接收器,以便集中收集远程日志
打包/分发
# Publish self-contained for ARM64
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-arm64
# Deploy binary directly to device filesystem
scp -r ./publish/* pi@device:/opt/myapp/
直接部署到设备文件系统。不使用应用商店或包管理器。
平台注意事项
- **无窗口系统:**不支持 X11/Wayland。应用直接渲染到 Linux framebuffer(
/dev/fb0) - **输入处理:**通过 Linux 输入事件(
/dev/input/event*)处理触控输入。默认没有鼠标光标 - **资源有限:**嵌入式设备通常受 RAM 和 CPU 资源限制。请仔细分析内存使用情况
- **无浏览器:**无法使用需要浏览器的 OAuth 流程。请使用设备代码流程或预先配置的令牌
- **显示分辨率:**必须与 framebuffer 分辨率一致。通过内核启动参数或
fbset进行设置
AOT/裁剪
强烈建议嵌入式环境使用 AOT,以满足资源有限和启动时间方面的要求。
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
InvariantGlobalization 会通过移除 ICU 数据(约 28 MB)来减小二进制文件大小。仅当应用不需要特定于区域设置的格式化时才使用。
行为差异
- **导航:**仅支持触控或硬件按钮。不支持键盘快捷键或手势栏
- **身份验证:**使用设备代码流程或预先配置的凭据。不支持基于浏览器的 OAuth
- **调试:**仅支持远程调试。不支持本地 IDE。请使用 SSH 调试或远程日志记录
跨目标行为差异
导航
| 目标 | 返回导航 | 深层链接 | 手势导航 |
|---|---|---|---|
| Web/WASM | 浏览器后退按钮 / Alt+Left | 基于 URL 的路由 | 无 |
| iOS | 从左侧边缘向右滑动 | URL 方案 / 通用链接 | 完整的手势支持 |
| Android | 硬件/软件返回按钮 | Intent 过滤器 / 应用链接 | 系统返回手势(Android 13+) |
| macOS | Cmd+[ / 工具栏返回 | URL 方案 | 无 |
| Windows | Alt+Left / 标题栏返回 | 协议激活 | 无 |
| Linux | Alt+Left / 键盘 | 无 | 无 |
| 嵌入式 | 仅硬件按钮 | 无 | 仅触控 |
身份验证
| 目标 | OAuth 流程 | 令牌存储 | 生物识别 |
|---|---|---|---|
| Web/WASM | 浏览器重定向/弹出窗口 | 浏览器安全存储 | WebAuthn(有限支持) |
| iOS | ASWebAuthenticationSession | Keychain | Touch ID / Face ID |
| Android | Chrome Custom Tabs | Android Keystore | BiometricPrompt |
| macOS | ASWebAuthenticationSession | Keychain | Touch ID |
| Windows | 系统浏览器 / WAM | Credential Manager | Windows Hello |
| Linux | 系统浏览器 | Secret Service API | 无 |
| 嵌入式 | 设备代码流程 | 基于文件(加密) | 无 |
调试工具
| 目标 | IDE 调试器 | 热重载 | 性能分析 |
|---|---|---|---|
| Web/WASM | VS / VS Code (CDP) | 是 | 浏览器 DevTools |
| iOS | VS(配对到 Mac)/ VS Code / Rider | 是 | Xcode Instruments |
| Android | VS / VS Code | 是 | Android Profiler |
| macOS | VS(配对到 Mac)/ VS Code / Rider | 是 | Xcode Instruments |
| Windows | Visual Studio | 是(+ Live Visual Tree) | VS Diagnostic Tools |
| Linux | VS Code / Rider | 是 | dotnet-counters / dotnet-trace |
| 嵌入式 | VS Code Remote | 是(SSH) | dotnet-trace(远程) |
Agent 注意事项
- 不要在检测逻辑中硬编码 TFM 版本。 使用与版本无关的 glob(
net*-ios、net*-android),以兼容 .NET 8 和未来的 .NET 版本。 - 不要假设所有目标都使用相同的调试工作流。 iOS 需要配置预配;Android 需要 ADB;WASM 使用浏览器 DevTools。每个目标都有不同的工具。
- 不要在 iOS 上使用依赖 JIT 的模式。 iOS 禁止 JIT 编译。使用
Reflection.Emit、Expression.Compile()或动态程序集加载的代码会在运行时失败。 - 不要忘记特定于平台的权限。 Android 运行时权限、iOS 权限声明、macOS 沙盒权限和 WASM CORS 策略各不相同,必须针对每个目标分别处理。
- 不要部署未经裁剪的 WASM 应用。 未裁剪的 WASM 包会超过 30MB。务必启用裁剪,并考虑在生产环境的 WASM 部署中使用 AOT。
- 不要假设所有目标都能访问文件系统。 WASM 没有文件系统;iOS/macOS 有沙盒限制;嵌入式设备可能只有只读存储。请使用 Uno Storage 抽象。
- 不要对所有目标使用相同的身份验证流程。 WASM 使用浏览器重定向,移动平台使用原生身份验证会话,嵌入式设备使用设备代码流程。身份验证必须针对每个目标分别配置。
先决条件
- .NET 8.0+ 及平台工作负载:
dotnet workload install ios android maccatalyst wasm-tools - 平台 SDK:Xcode(iOS/macOS)、Android SDK(Android)、GTK3(Linux)
- Uno Platform 5.x+