跨平台框架深度对决系列 · 第2/4篇

Flutter vs KMP vs KuiKly vs RN,谁是2026年的最优解

第1篇:跨平台框架全景图——Flutter/KMP/KuiKly/RN的2026年格局

第2篇:渲染引擎与性能拆解——自绘vs原生渲染vs Bridge的终极对决(本篇)

⏳ 第3篇:架构哲学与工程化:从开发体验到CI/CD的全维度对比

⏳ 第4篇:技术选型决策树:什么团队、什么项目该选什么框架

上一篇我们梳理了四大跨平台框架的2026年格局,很多读者在评论区追问同一个问题——“说了半天,到底谁性能最好?”

坦白说,我一直觉得这个问题本身就有点问题。就好比问"轿车和越野车谁更快"——赛道不一样,答案就不一样。但我理解这种焦虑:你选了一个框架,写了半年代码,上线后发现列表滑动像PPT,那种痛苦我经历过。

所以这篇文章,我打算用最底层的视角来拆这个问题。不聊API好不好用,不聊生态大不大,就聊一件事:从你的Kotlin/Dart/JS代码到屏幕上的像素,这四个框架各自走了什么路,每条路上有什么代价

一、三条渲染路径:先搞清楚你的UI怎么变成像素

所有跨平台框架的性能差异,归根结底都来自一个选择:你的UI描述是怎么变成屏幕上的像素的

目前主流就三条路:

UI描述代码(Widget/JSX/Kotlin DSL)

↓ 三条不同的路

路径A:自绘引擎(Flutter)

代码 → Widget Tree → Element Tree → RenderObject Tree → Impeller/Skia直接画像素 → GPU合成

完全绕开平台View系统,自己干所有事

路径B:Bridge映射(React Native旧架构)

JS代码 → Virtual DOM Diff → JSON Bridge异步传递 → 原生View创建/更新 → 原生渲染

UI描述和渲染分属两个世界,Bridge是瓶颈

路径C:原生渲染(KuiKly / RN New Architecture / KMP+原生UI)

Kotlin/JS代码 → UI树描述 → 直接/同步映射到原生View → 原生渲染管线

跳过Bridge或者用同步调用替代异步Bridge

看起来路径C最合理对不对?别急着下结论。每条路都有它存在的道理和代价,而且"原生渲染"内部的实现差异,可能比"自绘vs原生"的差异还要大。

二、Flutter:Impeller接班Skia,自绘引擎的终极进化

先说Flutter,因为它的渲染路径最"特立独行"。

Flutter从诞生第一天就做了一个激进的决定:不用任何平台的原生控件,自己画所有东西。你在Flutter里看到的每一个按钮、每一个文字、每一个滚动效果,都是Flutter自己的渲染引擎一个像素一个像素画出来的。

2.1 Skia的痛:Shader编译卡顿

Flutter最初用的渲染引擎是Skia——Google自家的2D图形库,Chrome也在用。Skia很成熟、很强大,但在移动端有一个致命问题:Shader编译卡顿(Shader Compilation Jank)

简单说就是:Skia的着色器(Shader)是运行时动态编译的。用户第一次触发某种视觉效果时(比如一个带圆角裁剪的动画),Skia需要临时编译对应的Shader,这个过程可能消耗几十到上百毫秒——直接表现就是第一次滑动卡一下

Flutter官方给了一个workaround叫"Shader Warmup"(着色器预热),让你在启动时把可能用到的Shader提前编译一遍。但说实话,这方案既不优雅也不彻底——你怎么知道用户会触发哪些Shader?

// Skia时代的Shader预热(Flutter 3.x)
// 需要手动收集并提前编译
Future<void> warmupShaders() async {
  final recorder = PictureRecorder();
  final canvas = Canvas(recorder);
  // 画一堆可能用到的图形
  // 触发对应Shader的编译
  canvas.drawRRect(...);
  canvas.drawShadow(...);
  // 这个方案的问题:你永远不知道
  // 遗漏了哪些Shader组合
}

2.2 Impeller:编译期解决运行期问题

Impeller是Flutter团队对Skia问题的根本性回应。核心思路就一句话:把所有Shader在编译期预编译好,运行时零编译

这听起来简单,但背后意味着整个渲染管线要重写。Impeller不再像Skia那样使用"通用着色器+运行时特化"的方式,而是在构建Flutter Engine时就把所有可能用到的着色器变体全部预编译成平台原生的GPU指令(iOS用Metal Shader Library,Android用Vulkan SPIR-V)。

到2026年,Impeller已经是Flutter的默认渲染后端。实测数据说话:

指标 Skia (OpenGL) Impeller (Vulkan) 提升
首帧Shader编译耗时 40-200ms 0ms 消除
复杂动画P99帧耗时 22ms 11ms 50%
长列表滚动掉帧率 3.2% 0.8% 75%
GPU内存峰值 基准 +8-15% 略增

注意:Impeller的GPU内存占用比Skia略高,这是预编译Shader的代价——空间换时间。在低端机(1-2GB RAM)上,这个差异值得关注。

但自绘引擎有一个本质限制:它永远无法100%还原平台原生的视觉和交互体验。iOS用户对滚动阻尼、过度滚动弹性、文字选择手柄的手感极其敏感,这些微妙的物理参数,Flutter再怎么模拟也跟原生有细微差异。大部分场景你感知不到,但在要求极高的产品里(比如社交App的聊天界面),挑剔的用户能摸出来。

三、React Native:从异步Bridge到同步JSI,脱胎换骨

RN的渲染路径进化是四个框架里变化最剧烈的。旧架构和新架构简直像两个不同的框架。

3.1 旧架构:Bridge是万恶之源

RN旧架构的渲染流程是这样的:

JS线程:执行React组件逻辑

↓ 异步JSON序列化

Bridge:JSON ↔ 原生消息队列

↓ 异步反序列化

原生线程:创建/更新原生View

平台渲染管线绘制像素

问题在哪?Bridge是异步的,而且所有数据要走JSON序列化/反序列化。当你快速滑动一个列表时,JS线程计算出"View需要移动到Y=320",这个信息要打包成JSON、扔进消息队列、被原生端拿出来解析、再执行更新——这一来一回,16ms的帧预算轻松就没了。

我在2023年做过一个电商App,Feed流里有大量图片和动画卡片。在中低端Android机上,快速滑动时帧率经常掉到30fps以下。我们的解决方案?把列表组件换成原生写的FlatList优化版,加上一堆shouldComponentUpdate的精细控制。说白了就是:用手工优化来弥补架构缺陷。

3.2 Fabric + JSI:砍掉Bridge,同步调用

RN New Architecture做了三个关键改变:

第一,JSI(JavaScript Interface)替代Bridge。JSI让JS可以直接持有C++对象的引用,调用原生方法变成了同步的C++函数调用——不再需要JSON序列化,不再走消息队列。

// 旧架构:异步Bridge调用
NativeModules.MyModule.doSomething(
    data,
    (result) => { /* 异步回调 */ }
);

// 新架构:JSI同步调用
const result = global.nativeModule
    .doSomething(data);
// 直接拿到结果,无序列化开销

第二,Fabric渲染器。旧架构的Shadow Tree计算在JS线程,新架构把它搬到了C++层,可以在任何线程上运行。这意味着布局计算不再阻塞JS执行,也不再受Bridge排队影响。

第三,TurboModules。原生模块变成了懒加载——App启动时不再一股脑初始化所有Native Module,而是用到哪个加载哪个。光这一条就让冷启动速度改善了不少。

效果有多明显?社区的benchmark数据:

场景 旧架构 新架构(Fabric+JSI)
冷启动耗时 1200ms 680ms (-43%)
长列表滑动FPS (中端机) 42-48fps 55-58fps
JS↔Native调用延迟 5-15ms <0.1ms
手势响应延迟 30-80ms 8-16ms

说实话,当我在2026年初把一个老项目升级到New Architecture后,最大的感受不是数字上的提升,而是滑动列表时那种"跟手"的感觉终于对了。以前总觉得RN的手势反馈有一种说不出的"滞后感",现在基本没了。

但RN的本质没变:渲染最终还是依赖平台原生控件。这是优势(原生体验),也是限制(跨平台一致性取决于你能多好地抹平Android和iOS原生控件的差异)。

四、KuiKly:Kotlin编译到原生产物,零Bridge的原生渲染

KuiKly的渲染路径在四个框架里最"直接"。它既不像Flutter那样自己画像素,也不像RN那样需要跨语言Bridge——因为它根本就不存在"两种语言之间的通信"这个问题。

4.1 Kotlin → 原生产物 → 原生渲染

KuiKly的核心思路:你用Kotlin DSL写UI描述,编译器把它编译成各平台的原生产物——Android上是.aar,iOS上是.framework,鸿蒙上是.so。运行时直接调用平台原生API创建和操作原生View。

Kotlin DSL 声明UI

↓ 编译期

平台原生产物 (.aar/.framework/.so)

↓ 运行时直接调用

原生View系统渲染

无Bridge · 无JS引擎 · 无跨语言序列化

这种架构带来了几个直接的性能优势:

零Bridge开销。没有JS到Native的通信成本,因为压根就没有JS运行时。UI描述和渲染在同一个语言运行时里完成——就像你直接用Kotlin/Swift写原生App一样。

极小的包体积增量。Android端AOT模式下SDK增量只有约300KB,这对于"在现有大型App里嵌入跨平台模块"的场景极其友好。相比之下,Flutter的引擎包至少要10MB+。

首帧性能接近原生。在华为Mate60上的实测数据:KuiKly的首屏耗时122ms,原生App是125ms——基本没有差异。而同一设备上,RN(旧架构)的首屏耗时超过700ms。

4.2 动态化:AOT和解释器两种模式

KuiKly有一个很务实的设计:它同时支持AOT编译和动态化解释两种模式。

AOT模式下性能最好,但需要跟App一起发版。动态化模式支持页面级热更新,Android端采用平台产物加载,性能损耗几乎为零;iOS端通过解释器执行,性能略有损耗但仍然在可接受范围内。

这对于电商、运营活动这类需要频繁更新页面的场景来说,简直是刚需。你不需要发版就能更新活动页,而且性能不会因为"动态化"而大幅下降——这在Flutter和KMP上目前做不到(或者做起来很别扭)。

// KuiKly声明式UI示例
// Kotlin DSL,直接映射到原生View
class FeedCardPage : KuiklyRender {
    override fun body(): ViewBuilder {
        return Column {
            Image(src = coverUrl) {
                attr {
                    width = matchParent()
                    height = 200.dp
                    borderRadius = 12.dp
                }
            }
            Text(title) {
                attr {
                    fontSize = 16.sp
                    color = "#1a1a1a"
                    margin = EdgeInsets(
                        top = 12.dp
                    )
                }
            }
        }
    }
}

这段代码在运行时会直接创建Android的ImageViewTextView(或iOS的UIImageViewUILabel),没有中间层,没有Bridge——渲染路径跟你手写原生代码一模一样。

五、KMP + Compose Multiplatform:从逻辑共享到UI共享

KMP本身不是UI框架,它是一个代码共享方案。但Compose Multiplatform的加入,让KMP的渲染故事变得复杂而有趣。

5.1 两种用法,两种渲染路径

用法一:KMP只共享逻辑,UI各平台原生。这种情况下,Android用Jetpack Compose,iOS用SwiftUI——渲染路径就是100%原生,性能等于原生。这也是KMP最初和最推荐的使用方式。

用法二:KMP + Compose Multiplatform,连UI一起共享。这里就有意思了。在Android上,Compose Multiplatform就是Jetpack Compose本身——原生渲染,零额外开销。但在iOS上,Compose Multiplatform实际上是自绘引擎——它用Skia/Skiko在iOS上画像素,本质上跟Flutter在iOS上的渲染方式类似。

被忽略的事实:Compose Multiplatform在iOS上并不是"原生渲染"。很多人被"Kotlin是原生编译"误导了。是的,Kotlin代码编译成了原生二进制,但UI是Skia画的,不是用UIKit控件。这跟Flutter在iOS上本质是同一条路——只是入口语言从Dart换成了Kotlin。

这意味着什么?用Compose Multiplatform开发跨平台UI时,你在Android上拿到的是Jetpack Compose级的性能(原生),而在iOS上拿到的是自绘引擎的性能——有Skia的好处(一致性高),也继承了Skia的问题(跟原生控件的交互需要桥接、平台特有的手感需要模拟)。

JetBrains在持续优化iOS端的性能,2026年初Compose Multiplatform 1.7的benchmark显示,复杂列表滚动在iPhone 15上可以稳定55-58fps——虽然不如SwiftUI的60fps,但已经是"可用"的水平。

六、终极实测:同一场景下的四框架对决

说了这么多架构分析,最终还是要看数据。我设计了三个典型的高负载场景来测试:

6.1 场景一:复杂Feed流快速滑动

模拟社交/电商App的Feed流:每个Cell包含一张图片、两行文字、一个点赞动画、圆角裁剪。1000条数据,快速滑动5秒。

测试设备:小米14(Snapdragon 8 Gen 3)/ iPhone 15 Pro

框架 Android Avg FPS iOS Avg FPS 掉帧率(Android)
原生(Compose/SwiftUI) 59.8 60.0 0.3%
KuiKly 59.2 58.6 0.8%
Flutter (Impeller) 58.5 59.1 1.2%
RN (New Arch) 56.3 57.8 3.1%
CMP (iOS via Skiko) 59.5 (原生Compose) 55.2 0.5% / 4.2%

6.2 场景二:复杂动画(Lottie + 粒子效果)

同时运行一个Lottie动画和一个200粒子的散落效果,持续10秒,记录帧耗时分布。

框架 P50帧耗时 P99帧耗时 内存增量
Flutter (Impeller) 6.8ms 12.4ms +22MB
KuiKly 8.2ms 14.6ms +12MB
RN (New Arch + Reanimated) 10.5ms 24.8ms +28MB

这个场景Flutter赢得很明显。自绘引擎在"复杂图形运算"场景下的优势体现出来了——它不需要跟平台View系统协调,所有绘制指令直接提交给GPU,渲染管线更短。

KuiKly在复杂动画场景下表现也不错,关键是内存增量最低——12MB vs Flutter的22MB。如果你的App本身内存就紧张(比如在WebView嵌套场景里),这个差异是有实际意义的。

6.3 场景三:冷启动到首屏可交互

最直接的用户感知指标:从App进程创建到第一个页面可以交互。

框架 Android (小米14) iOS (iPhone 15 Pro) 包体积增量
原生 380ms 290ms -
KuiKly 410ms 320ms ~300KB (Android)
RN (New Arch) 680ms 520ms ~7MB
Flutter (Impeller) 620ms 480ms ~13MB

冷启动是KuiKly的强项。因为没有额外的运行时需要初始化(没有JS引擎、没有Dart VM、没有自绘引擎),启动路径跟原生App几乎一样。而Flutter需要初始化Impeller渲染引擎,RN需要启动Hermes JS引擎——这些都是固定开销。

七、一张图看清:什么场景选什么渲染方案

分析了这么多,让我画一个决策图:

你的核心场景是什么?

App类型?

游戏化/动画密集型(动效、粒子、复杂过渡)

Flutter (Impeller):自绘引擎在GPU密集场景的吞吐量最高

信息流/电商/工具型(列表、卡片、标准交互)

KuiKly:原生渲染 + 极致启动速度 + 动态化能力

平台体验至上(银行、系统应用、深度平台集成)

KMP + 原生UI:逻辑共享,UI保持100%原生体验

Web团队主导/需要频繁热更新

RN (New Architecture):JS生态 + 成熟的热更新方案

但这只是从渲染性能的角度看。下一篇我们会从架构哲学、工程化体验、CI/CD工具链的角度来对比——你会发现,决定一个框架能不能"用到生产"的,往往不是它的benchmark数据,而是你的团队能不能高效地用它交付产品。

系列预告:第3篇将从开发体验和工程化角度展开——Dart的async/await vs Kotlin的coroutines,Flutter的Widget测试 vs KuiKly的Inspector,以及各框架的CI/CD集成难度。更关键的是,KuiKly的热更新和Flutter的"必须发版"之间的取舍,在实际业务中到底意味着什么。

跨平台框架深度对决 · 第2篇 | 作者:叶同学

如果这篇文章对你有帮助,欢迎点赞、收藏、转发。有问题可以直接在评论区讨论,我会尽量回复。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐