☰

跨平台开发大致有两条路线:

  1. 共享业务逻辑,UI 各写各的:以 Kotlin Multiplatform(KMP)为代表。网络、数据、状态管理和业务规则只实现一次,Android 使用 Compose,iOS 使用 SwiftUI,保留原生 UI 控制力。
  2. 共享 UI,一套代码跑多端:以 Flutter、Compose Multiplatform、React Native、uni-app 为代表。页面、组件和交互逻辑尽量复用,再通过自绘、原生组件桥接或编译适配不同平台。

理解这一点,后续的框架对比才有意义。跨平台方案真正的差别,不只是“支持多少平台”,还包括 UI 由谁渲染、平台能力如何调用,以及代码最终运行或编译到哪里。

一、先厘清:跨平台有两条路线

路线一:共享业务逻辑,UI 各写各的

这种路线的核心是:复用业务规则,不强行复用用户界面。

1共享层:网络、数据、缓存、状态、业务规则
2                    ↓
3平台层:页面、导航、交互、系统能力
4                    ↓
5        Android Compose + iOS SwiftUI

它适合 Android 和 iOS 的产品体验差异明显、原生能力较多,或者已经存在成熟原生客户端的团队。代价是页面、导航和部分状态展示需要分别维护。

路线二:共享 UI,一套代码跑多端

这条路线同时复用业务逻辑和 UI,通过跨平台框架覆盖 Android、iOS、Web、桌面、小程序或鸿蒙等目标端。

1一套代码:页面、组件、状态、业务逻辑
2                    ↓
3       Android / iOS / Web / Desktop / 小程序

它适合页面结构和交互流程相近、强调多端一致性和交付速度的产品。平台差异越大,额外适配代码越多;如果产品核心竞争力是深度原生体验,UI 复用率高未必是优势。

二、Dart 与 Flutter 快速回顾

Flutter 使用 Dart 编写,是当前跨平台客户端开发中具有代表性的技术栈。它通过 Flutter Engine 自己绘制大部分 UI,而不是主要依赖 Android 或 iOS 的原生按钮、列表等控件。

1Dart UI 代码
2    ↓
3Flutter Framework
4    ↓
5Flutter Engine
6    ↓
7Android / iOS / Web / Desktop

Dart 的核心特性

  • 健全空安全:通过类型系统和编译期检查减少空引用异常。
  • JIT + AOT 双编译:开发模式使用 JIT 支持热重载;发布模式使用 AOT 预编译为原生机器码。
  • Isolate 并发模型:Isolate 之间不共享内存,主要通过消息通信,降低共享状态导致的竞态风险。
  • 现代语言特性:支持模式匹配、Records、扩展方法和 async/await 等特性。
  • Pub 生态:pub.dev 提供了大量第三方包和 Flutter 插件。

Dart 有运行时吗?

有。Dart VM 是 Dart 的完整运行时,包含对象模型、垃圾回收、快照和编译管道等部分。开发模式下通常使用 JIT;发布模式下通常使用 AOT 预编译代码运行。

因此,Dart 不是传统意义上的“解释器”。更准确的说法是:Dart 在开发阶段依赖 VM 的 JIT 能力,在发布阶段使用 AOT 产物,并由 Flutter Engine 提供跨平台运行基础。

Dart 与 Java 的性能对比

性能必须结合场景讨论,不能用单一数字概括:

  • 启动速度:Dart AOT 不需要在设备上等待完整的运行时编译,适合移动端冷启动以及部分 CLI、FaaS 场景。
  • 长时间吞吐:Java HotSpot JIT 可以根据运行时反馈持续优化,服务端长时间运行时的峰值吞吐通常更有优势。
  • 内存占用:Dart/Flutter 需要携带自己的运行时和渲染基础设施;移动端内存占用可能高于直接使用平台原生技术,实际差距取决于版本、页面和业务负载。
  • 并发模型:Java 常见模型是共享内存多线程,通信成本低但同步复杂;Dart Isolate 采用隔离内存,状态更安全,但创建和消息传递也有成本。

结论可以简化为:**Dart 更强调启动表现和开发效率,Java 更适合长时间运行、需要依赖 HotSpot 优化的服务端峰值吞吐场景。**这不是移动端 Flutter 与 Java Android 应用的简单性能排名。

三、KMP:共享逻辑,接近原生

Kotlin Multiplatform(KMP)的核心哲学是:共享业务逻辑,UI 各写各的。公共模块使用 Kotlin 编写,在多个平台复用;Android 和 iOS 分别使用自己的 UI 技术和平台 API。

KMP 通常共享什么?

  • 网络请求与 API 封装;
  • 数据模型与序列化;
  • 本地数据库、缓存和同步;
  • 状态管理;
  • 数据校验、分页和错误处理;
  • 登录、权限和业务规则。

Android 可以使用 Kotlin + Jetpack Compose,iOS 可以使用 Swift + SwiftUI。摄像头、推送、后台任务、支付和蓝牙等能力,则可以通过平台实现或在共享层定义接口后分别接入。

KMP 的优势

  • 共享逻辑后的运行性能和内存开销通常接近原生实现;
  • 对 Android/Kotlin 团队学习曲线相对平缓;
  • 可以逐步迁移存量项目,而不是一次性重写全部 UI;
  • UI 和系统能力保留原生控制力;
  • 社区中已经出现面向鸿蒙的适配探索和实践案例。

KMP 的缺点:不只是没有热重载

  • 缺少 Flutter 式热重载:修改共享代码后,通常需要重新编译或重新运行目标平台。
  • 编译反馈可能较慢:Kotlin/Native 编译耗时受工程规模、缓存和机器环境影响,增量编译稳定性也需要验证。
  • 调试链路割裂:共享 Kotlin 代码、Xcode、iOS 符号和平台调试工具之间存在工具链边界。
  • iOS 互操作有摩擦:sealed class、协程、泛型等 Kotlin 语义映射到 Swift/Objective-C 后,表达能力和使用体验可能发生变化。
  • 部分垂直领域生态不如 Flutter 丰富:BLE、后台服务、音视频流等能力需要重点核验库的维护状态和平台覆盖。
  • 工程复杂度较高:Gradle、Kotlin/Native、CocoaPods 或 Swift Package Manager,以及多平台 CI 需要统一管理。
  • 鸿蒙支持不能默认视为官方能力:JetBrains 和华为是否直接支持某个目标,必须以当前版本官方文档为准;社区编译器和适配方案需要单独评估维护风险。

KMP 支持哪些平台?

KMP 的平台支持由 Kotlin 编译器、库以及具体框架版本共同决定,不能只看语言层面的 target。大致可以按以下类别理解:

编译目标常见平台成熟度说明
Kotlin/JVMAndroid、JVM 桌面、服务端Android 和 JVM 生态成熟
Kotlin/NativeiOS、macOS主要移动和 Apple 平台较成熟
Kotlin/NativewatchOS、tvOS、Linux、Windows需结合具体库与工具链核验
Kotlin/JSWeb适合 JavaScript 目标
Kotlin/WasmWebAssembly仍需关注版本与库兼容性
Kotlin/Wasm/WASIWASI 环境面向特定 Wasm 运行环境
鸿蒙社区 Kotlin/Native 适配方案不应等同于官方直接支持

版本更新较快。项目立项时,应针对目标平台执行真实编译、调试和关键库验证,而不是仅依据宣传页上的平台列表做决定。

四、主流跨平台方案横向对比

方案核心哲学典型平台UI 实现方式性能与一致性鸿蒙适配最佳场景
Flutter自绘引擎,UI 高度一致移动、Web、桌面Flutter Engine 绘制一致性高,性能高需核验社区方案新项目、定制 UI、复杂动效
React Native复用前端栈,驱动平台组件Android、iOS,另有桌面生态原生组件桥接/新架构运行时原生感较强,跨端一致性中等需核验社区方案前端团队快速构建移动端
Compose Multiplatform用 Kotlin/Compose 共享 UIAndroid、iOS、桌面,Web 能力持续演进Compose 跨平台渲染性能较高,Kotlin 生态衔接好需社区方案Kotlin 团队共享 UI
KuiklyKMP UI 层与原生渲染结合Android、iOS、鸿蒙、H5、小程序跨平台描述与平台渲染适配复杂动效和原生能力表现需实测方案重点之一Kotlin 团队且必须覆盖鸿蒙
uni-appVue 语法,编译适配多端移动、鸿蒙、Web、小程序按目标端编译或运行一致性和性能依目标端变化国内生态支持较方便小程序 + App + H5 快速覆盖
TauriRust 后端 + 系统 WebView桌面,也支持部分移动端Web UI + 系统 WebView产物小,依赖 WebView无统一官方方案轻量桌面工具
.NET MAUIC#/.NET 统一开发Android、iOS、macOS、Windows原生控件抽象企业场景较稳,需处理平台差异无统一官方方案已有 .NET 技术栈的企业
QtC++/QML,工业级跨平台桌面、移动、嵌入式、WebQt 渲染与原生能力性能和控制力强需单独评估工业软件、嵌入式、高性能桌面
SkipSwiftUI 映射到 Android ComposeiOS、AndroidSwiftUI 编译/映射为 Compose复用 SwiftUI,生态仍在发展无统一官方方案已有 SwiftUI 代码库扩展 Android

这里的“鸿蒙支持”不能简单理解为“能否运行”。需要进一步确认:是否有官方 SDK 支持、是否能调用系统能力、插件是否覆盖关键业务、构建工具是否纳入 CI,以及出现问题后由谁维护。

五、几个关键区别:自绘、原生桥接与编译到多端

Flutter:自绘 UI

Flutter 不主要依赖 Android 或 iOS 的原生控件,而是通过自己的渲染引擎绘制界面。

核心是:自己绘制 UI,平台主要提供运行环境和系统能力。

优势是视觉一致、动画和自定义渲染能力强;代价是原生控件行为、无障碍、输入法、平台手势等细节需要主动验证。摄像头、通知、支付等系统能力通常依赖插件或平台通道。

React Native:原生组件桥接

React Native 使用 JavaScript/TypeScript 描述 UI,再映射或驱动 Android、iOS 的原生视图。新架构改善了部分通信路径和渲染链路,但复杂能力仍然需要 Native Module、原生组件扩展或平台专属代码。

核心是:共享 React UI 描述,通过运行时驱动平台组件。

它对前端团队友好,原生感通常较好;但版本升级、第三方库质量和原生桥接维护成本需要纳入长期预算。

Compose Multiplatform:共享 Compose UI

Compose Multiplatform 使用 Kotlin 和 Compose 描述界面,让一套声明式 UI 代码运行到 Android、iOS、桌面等目标。它与 KMP 的共享业务逻辑能力天然衔接,但“共享 UI”不等于所有平台都使用系统原生控件。

核心是:用 Kotlin 统一描述 UI,并由 Compose Multiplatform 运行时适配不同平台。

uni-app:编译到多端

uni-app 以 Vue/JavaScript/TypeScript 为主要开发方式,根据目标平台生成不同形式的代码或运行结果:

  • Web:H5;
  • 微信等平台:小程序代码;
  • App:根据具体模式使用 WebView、原生渲染或混合方案;
  • 鸿蒙:以当前版本和目标能力支持情况为准。

核心是:一套前端代码,通过编译器和运行时适配多个端。

它在国内小程序生态中优势明显,适合内容、商城、运营和表单类应用。但跨端组件、生命周期和系统能力并不完全相同,关键页面必须在每个目标端实测。

六、场景化选型建议

1. Kotlin/Android 背景,且必须覆盖鸿蒙

优先评估 Kuikly。它以 Kotlin/KMP 体系为基础,强调跨平台 UI、原生渲染和鸿蒙覆盖。腾讯内部有多款产品实践,但项目仍应核验当前版本的开源范围、社区响应、插件能力和团队可控性。

如果选择 Kuikly,建议在立项阶段先做一个包含登录、列表、输入、图片、网络请求、推送和鸿蒙系统能力调用的垂直切片,而不是只验证一个静态页面。

2. Kotlin/Android 背景,希望共享 UI,但不强求鸿蒙

选择 Compose Multiplatform。它可以复用 Compose DSL、状态和业务逻辑,Android 团队迁移成本相对可控,适合覆盖 Android、iOS 和桌面。

iOS 端的自定义字体、键盘、输入法、导航、无障碍和系统组件行为仍需单独验证。共享 UI 的边界也应保留逃生通道,避免为了统一页面而牺牲平台体验。

3. 前端 JavaScript/TypeScript 背景

  • 想快速构建移动 App:优先评估 React Native,并确认所需库是否支持当前新架构。
  • 想覆盖国内小程序、App 和 H5:优先评估 uni-app,重点验证复杂交互和原生能力。
  • 想要高度一致的自定义 UI:也可以评估 Flutter,但团队需要接受 Dart 和 Flutter 生态。

4. iOS/Swift 背景,已有 SwiftUI 代码库

关注 Skip。它尝试把 SwiftUI 代码扩展到 Android,并映射为 Android Compose。适合已有 SwiftUI 资产的团队,但项目应重点检查开源协议、编译边界、第三方库兼容性和长期维护风险。

5. 高性能桌面或工业软件

  • 工业级软件、嵌入式和复杂桌面应用:优先评估 Qt,C++/QML 提供较强的性能与平台控制力。
  • 轻量桌面工具:优先评估 Tauri,Rust 后端结合系统 WebView,通常可以减少产物体积;实际体积仍取决于目标平台、资源和打包方式,不能把某个示例体积当作普遍保证。

6. 企业 .NET 技术栈

选择 .NET MAUI,前提是团队已有 C#、.NET、CI/CD 和企业级客户端维护能力。它能减少技术栈分裂,但仍需评估各平台控件、原生 API 和发布链路的差异。

7. 全新项目,UI 高度定制

选择 Flutter。自绘引擎、热重载、组件生态和跨端一致性适合快速迭代高度定制的界面。若产品强依赖平台原生控件、无障碍或系统级交互,则应先做原生能力验证。

七、做决定前的验证清单

不要只用 Demo 的首屏速度或静态页面决定框架。至少验证以下内容:

  • 目标平台是否包含鸿蒙、小程序、桌面或 Web;
  • 登录、网络错误、离线缓存、分页和状态恢复是否能共享;
  • 键盘、输入法、权限、推送、后台任务和深链是否可用;
  • 列表滚动、复杂动效、图片加载和低端设备内存表现;
  • 核心插件的维护者、版本兼容性和许可证;
  • Debug、Profile、Release 三种模式下的构建与调试体验;
  • CI 构建时间、签名、商店发布和崩溃定位链路;
  • 平台专属代码的比例,以及未来由谁维护;
  • 关键框架或社区方案停止维护后的迁移路径。

一个可执行的办法是:为候选框架分别实现同一个垂直切片,然后比较交付时间、包体积、启动、内存、滚动帧率、原生能力接入和问题定位成本。这样得到的结论比“谁的宣传页支持平台更多”可靠。

八、总结:没有最好,只有最合适

跨平台选型不是比较谁“更强”,而是看谁更匹配以下条件:

  • 团队基因:Android、iOS、前端还是 .NET?
  • 目标平台:是否必须覆盖鸿蒙?是否需要小程序?
  • 性能要求:关注启动速度、内存、复杂动效,还是长时间吞吐?
  • 生态需求:核心库是否成熟?BLE、音视频、后台服务等垂直领域是否有可靠实现?
  • 开发体验:热重载、调试能力和编译速度是否能接受?
  • 平台控制力:产品是否依赖原生交互、系统能力和无障碍?

一句话速查:

  • 要鸿蒙 + Kotlin:评估 Kuikly;
  • 要共享 UI + Kotlin:选择 Compose Multiplatform;
  • 要前端快速开发:选择 React Native;
  • 要小程序全覆盖:选择 uni-app;
  • 要 UI 极致一致:选择 Flutter;
  • 要工业级性能:选择 Qt;
  • 要轻量桌面:选择 Tauri;
  • 要 Swift 扩展 Android:关注 Skip;
  • 要共享逻辑并保留原生 UI:选择 KMP。

跨平台开发没有银弹。KMP 不是唯一答案,热重载也不是它唯一的短板;Flutter、React Native、Compose Multiplatform、Kuikly、uni-app、Tauri、.NET MAUI、Qt 和 Skip 都有各自的取舍。真正稳妥的方案,是先明确共享边界,再用目标平台的真实垂直切片验证,而不是先选一个框架再迁就产品需求。