dotnet-uno目标部署技能 dotnet-uno-targets

本技能提供Uno Platform应用程序的跨平台部署详细指南,涵盖Web/WASM、iOS、Android、macOS、Windows、Linux和嵌入式平台。内容包括项目设置、调试工作流、打包分发、平台特定问题及AOT/修剪优化。关键词:Uno Platform, 跨平台部署, WASM, iOS, Android, macOS, Windows, Linux, 嵌入式, AOT编译, 代码修剪。

移动开发 0 次安装 0 次浏览 更新于 3/6/2026

名称: dotnet-uno-目标部署 描述: “部署Uno Platform应用程序。针对WASM、iOS、Android、macOS、Windows、Linux和嵌入式平台的具体指导。” 用户可调用: false

dotnet-uno-目标部署

针对Uno Platform应用程序的跨平台部署指南:Web/WASM、iOS、Android、macOS (Catalyst)、Windows、Linux (Skia/GTK) 和嵌入式 (Skia/Framebuffer)。每个目标部分涵盖项目设置、调试工作流、打包/分发、平台特定问题、AOT/修剪影响以及与其他目标的行为差异。

范围

  • 各目标项目设置:WASM、iOS、Android、macOS (Catalyst)、Windows、Linux、嵌入式
  • 每个目标平台的调试工作流
  • 打包和分发(App Store、Play Store、MSIX、Flatpak)
  • 平台特定问题和AOT/修剪影响

范围外

  • 核心Uno Platform开发(扩展、MVUX、工具包)-- 参见 [技能:dotnet-uno-platform]
  • 实时文档的MCP集成 – 参见 [技能:dotnet-uno-mcp]
  • Uno Platform测试 – 参见 [技能:dotnet-uno-testing]
  • 通用AOT/修剪模式 – 参见 [技能:dotnet-aot-wasm]
  • UI框架选择 – 参见 [技能:dotnet-ui-chooser]

交叉引用:核心开发参见 [技能:dotnet-uno-platform],MCP集成参见 [技能:dotnet-uno-mcp],测试参见 [技能:dotnet-uno-testing],通用WASM AOT模式参见 [技能:dotnet-aot-wasm],框架选择参见 [技能:dotnet-ui-chooser]。


目标平台概述

目标 TFM 工具 打包 关键限制
Web/WASM net8.0-browserwasm 浏览器开发工具 静态托管 / Azure SWA 无文件系统访问,推荐AOT,线程有限
iOS net8.0-ios Xcode / VS Code / Rider App Store / TestFlight 配置配置文件,授权,无JIT
Android net8.0-android Android SDK / 模拟器 Play Store / APK侧载 SDK版本目标,权限
macOS (Catalyst) net8.0-maccatalyst Xcode Mac App Store / 公证 沙箱限制,授权
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备注: 在编程检测平台目标时,使用版本无关的通配符(net*-iosnet*-android),以避免在旧版或新版TFM上出现误报。


Web/WASM

项目设置

# 运行WASM目标
dotnet run -f net8.0-browserwasm --project MyApp/MyApp.csproj

WASM目标在浏览器中渲染XAML控件。渲染器取决于项目配置:Skia(画布/WebGL)或原生HTML映射。应用程序通过JavaScript引导程序(uno-bootstrap.js)加载,初始化.NET WASM运行时。

调试

  • 浏览器开发工具: 在Chrome/Edge中使用F12检查DOM、网络、控制台输出
  • .NET调试: Visual Studio和VS Code支持通过浏览器CDP调试.NET WASM运行时
  • Uno开发服务器: dotnet run 启动一个具有实时重载支持的开发服务器
  • 控制台日志: ILogger 输出出现在浏览器控制台中

打包/分发

# 为生产环境发布
dotnet publish -f net8.0-browserwasm -c Release --output ./publish

# 输出是静态站点(HTML/JS/WASM)
# 部署到:Azure静态Web应用、GitHub Pages、Netlify、任何静态主机

发布输出是自包含的静态站点,无需服务器端运行时。

平台问题

  • 无文件系统访问: 通过JS互操作或Uno.Storage使用浏览器存储API(IndexedDB、localStorage)
  • 线程限制: Web Workers提供有限的多线程;Task.Run 在WASM上可能无法并行化
  • CORS限制: WASM中的HTTP请求受浏览器CORS策略约束
  • 初始加载时间: .NET WASM运行时和程序集必须下载后应用才可用。使用程序集修剪来减少下载大小。AOT提高运行时执行速度但增加工件大小
  • 深度链接: 在Uno导航扩展中配置URL路由,以支持浏览器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模式,参见 [技能:dotnet-aot-wasm]。

行为差异

  • 导航: 基于URL的深度链接原生工作;路由映射应定义URL模式
  • 认证: OAuth流程使用浏览器重定向(弹出或重定向);无原生浏览器可用。令牌存储使用浏览器安全存储
  • 调试: 通过CDP提供完整.NET调试器;断点在Visual Studio/VS Code中工作
  • 文件选择器: 使用浏览器文件输入API;FileOpenPicker 映射到 <input type="file">

iOS

项目设置

# 为iOS模拟器构建
dotnet build -f net8.0-ios

# 在模拟器上运行
dotnet run -f net8.0-ios

需要在macOS上安装Xcode。渲染器(Skia或原生)取决于项目配置和Uno版本。

调试

  • Visual Studio(配对到Mac)/ VS Code + C#开发套件 / Rider: 附加到iOS模拟器或设备
  • Xcode Instruments: 分析CPU、内存和能源使用
  • 热重载: 通过 DOTNET_MODIFIABLE_ASSEMBLIES=debug 支持

打包/分发

# 为App Store发布
dotnet publish -f net8.0-ios -c Release \
  /p:CodesignKey="Apple Distribution: MyCompany" \
  /p:CodesignProvision="MyApp Distribution Profile"

分发渠道:App Store(需要Apple开发者账户)、TestFlight(beta测试)、Ad Hoc(企业)。

必需: 配置配置文件、签名证书、用于功能(推送通知、HealthKit等)的授权文件。

平台问题

  • 无JIT编译: iOS禁止JIT。所有代码必须AOT编译或解释。.NET运行时默认使用Mono解释器
  • 配置配置文件: 必须匹配捆绑ID、团队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导航自动实现
  • 认证: 使用 ASWebAuthenticationSession 进行OAuth;通过 LocalAuthentication 框架进行生物识别认证
  • 调试: 模拟器快速;设备调试需要USB连接和配置。支持热重载的远程调试

Android

项目设置

# 为Android构建
dotnet build -f net8.0-android

# 部署到连接的设备或模拟器
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: 在模拟器或设备上完整调试,带断点

打包/分发

# 发布签名的APK/AAB(使用环境变量或CI密码,切勿硬编码)
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应用捆绑包(AAB)格式。侧载使用APK。

平台问题

  • SDK版本目标: <SupportedOSPlatformVersion> 控制最低API级别。目标API 34+以符合Google Play要求
  • 运行时权限: 危险权限(相机、位置、存储)需要运行时请求。在代码中检查和请求,不仅仅是清单
  • 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模拟器慢但支持更多API级别。使用硬件加速(HAXM/KVM)以提高性能

macOS (Catalyst)

项目设置

# 为macOS构建
dotnet build -f net8.0-maccatalyst

# 在macOS上运行
dotnet run -f net8.0-maccatalyst

使用Mac Catalyst(iOS API适配于macOS)。需要在macOS上安装Xcode。

调试

  • Visual Studio(配对到Mac)/ VS Code + C#开发套件 / Rider: 原生macOS调试
  • Xcode Instruments: 分析性能和内存
  • Console.app: 查看应用进程的系统日志

打包/分发

# 为分发发布
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

项目设置

# 为Windows构建
dotnet build -f net8.0-windows10.0.19041

# 在Windows上运行
dotnet run -f net8.0-windows10.0.19041

Windows目标可以使用Skia渲染器或原生WinAppSDK/WinUI 3渲染。

调试

  • Visual Studio: 完整调试,带XAML热重载、实时可视化树、实时属性资源管理器
  • WinUI诊断: 内置诊断覆盖用于布局检查

打包/分发

# 打包为MSIX
dotnet publish -f net8.0-windows10.0.19041 -c Release \
  /p:PackageOutputPath=./packages

分发:Microsoft Store(MSIX)、带证书的侧载、ClickOnce或直接EXE。

平台问题

  • WinAppSDK版本对齐: Windows TFM版本必须匹配最低Windows版本。10.0.19041 = Windows 10 2004+
  • UAC和提升: 应用无法自我提升。为标准用户权限设计
  • Windows特定API: Windows.StorageWindows.Networking API仅在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账户管理器)进行Microsoft账户的SSO
  • 调试: 最丰富的调试体验,带Visual Studio实时可视化树和XAML热重载

Linux (Skia/GTK)

项目设置

# 为Linux桌面构建
dotnet build -f net8.0-desktop

# 在Linux上运行
dotnet run -f net8.0-desktop

Linux目标使用Skia渲染器与GTK主机窗口。所有XAML渲染由Skia完成——GTK仅提供窗口和输入处理。

调试

  • VS Code带C#开发套件: Linux上的远程调试
  • JetBrains Rider: 完整Linux调试支持
  • 控制台日志: dotnet run 输出日志到终端

打包/分发

# 为Linux发布自包含
dotnet publish -f net8.0-desktop -c Release \
  --self-contained \
  -r linux-x64

# 打包为AppImage、Flatpak或Snap

分发:AppImage(便携,无需安装)、Flatpak(沙箱化)、Snap(Ubuntu商店)、DEB/RPM包。

平台问题

  • GTK依赖: 应用在运行时需要GTK3库。打包或记录依赖:libgtk-3-0libskia*
  • 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调试。无平台特定调试器工具

Embedded (Skia/Framebuffer)

项目设置

嵌入式目标使用Skia渲染器与framebuffer后端,实现无窗口系统或无头渲染。

# 为嵌入式构建(与桌面相同TFM)
dotnet build -f net8.0-desktop -r linux-arm64

# 直接在嵌入式设备上运行
dotnet run -f net8.0-desktop

嵌入式目标与Linux桌面共享 net8.0-desktop TFM。平台特定配置选择framebuffer后端。

// Program.cs -- framebuffer主机配置
public static void Main(string[] args)
{
    SkiaHostBuilder.Create()
        .UseFrameBuffer()  // 使用framebuffer代替GTK
        .App(() => new App())
        .Build()
        .Run();
}

调试

  • SSH远程调试: 使用VS Code Remote或 dotnet-trace 进行远程性能分析
  • 串行控制台: 将日志重定向到串行输出,用于无头调试
  • 远程日志: 配置带网络接收器的Serilog,用于远程日志聚合

打包/分发

# 为ARM64发布自包含
dotnet publish -f net8.0-desktop -c Release \
  --self-contained \
  -r linux-arm64

# 直接部署二进制文件到设备文件系统
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数据减少二进制大小(约28MB)。仅当应用不需要区域特定格式时使用。

行为差异

  • 导航: 仅触摸或硬件按钮。无键盘快捷键或手势栏
  • 认证: 设备码流或预配置凭据。无基于浏览器的OAuth
  • 调试: 仅远程。无本地IDE。使用SSH调试或远程日志

跨目标行为差异

导航

目标 后退导航 深度链接 手势导航
Web/WASM 浏览器后退按钮 / Alt+Left 基于URL的路由
iOS 从左侧边缘滑动 URL方案 / 通用链接 完整手势支持
Android 硬件/软件后退按钮 意图过滤器 / 应用链接 系统后退手势(Android 13+)
macOS Cmd+[ / 工具栏后退 URL方案
Windows Alt+Left / 标题栏后退 协议激活
Linux Alt+Left / 键盘
Embedded 仅硬件按钮 仅触摸

认证

目标 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 凭据管理器 Windows Hello
Linux 系统浏览器 秘密服务API
Embedded 设备码流 基于文件(加密)

调试工具

目标 IDE调试器 热重载 性能分析
Web/WASM VS / VS Code(CDP) 浏览器开发工具
iOS VS(配对到Mac)/ VS Code / Rider Xcode Instruments
Android VS / VS Code Android性能分析器
macOS VS(配对到Mac)/ VS Code / Rider Xcode Instruments
Windows Visual Studio 是(+ 实时可视化树) VS诊断工具
Linux VS Code / Rider dotnet-counters / dotnet-trace
Embedded VS Code Remote 是(SSH) dotnet-trace(远程)

代理问题

  1. 不要在检测逻辑中硬编码TFM版本。 使用版本无关的通配符(net*-iosnet*-android以处理.NET 8和未来.NET版本。
  2. 不要假设所有目标共享相同的调试工作流。 iOS需要配置;Android需要ADB;WASM使用浏览器开发工具。每个目标有独特的工具。
  3. 不要在iOS上使用依赖JIT的模式。 iOS禁止JIT编译。使用 Reflection.EmitExpression.Compile() 或动态程序集加载的代码将在运行时失败。
  4. 不要忘记平台特定权限。 Android运行时权限、iOS授权、macOS沙箱权限和WASM CORS策略都不同,必须按目标处理。
  5. 不要部署未修剪的WASM应用。 未修剪的WASM捆绑包超过30MB。始终启用修剪,并考虑生产WASM部署使用AOT。
  6. 不要假设所有目标都有文件系统访问。 WASM无文件系统;iOS/macOS有沙箱限制;嵌入式可能有只读存储。使用Uno存储抽象。
  7. 不要对所有目标使用相同的认证流程。 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+

参考文献