Flutter、RN、uni-app 我全在生产环境踩过一遍,说点选型对比表里不会写的
先说个前提,我不是那种把三个框架都写个 Demo 就出来排座次的人。过去四年我参与过四个 App:一个用 uni-app 起步后期大量回退原生、一个 RN 0.59 升到 0.73 的老项目、一个差点上 Flutter 但死在需求评审的营销工具、还有一个纯原生 Kotlin + Swift 的小工具 App。所以下面的东西基本都是花钱和熬夜换来的,不是 GitHub star 排名换来的。
如果你正在搜「跨端框架选型 2025」这类词,大概率能看到一堆性能对比表格:启动时间、内存占用、帧率、包体积,数据精确到小数点后两位。我建议你看完就忘掉。不是这些数据假,是它们跟你的项目基本没关系。下面我按我自己排的优先级讲,不按市面上那套。
一、性能对比这件事,我实测过,但结论是「别测」
我拿同一台 Redmi K40(骁龙 870,Android 13)测过同一个场景:一个 200 条数据的商品列表,每项含图片、标题、两个标签、一个加购按钮,快速滑动到底再滑回来。
- Flutter 3.16 release 构建,稳定在 58-60 帧,几乎不掉;
- RN 0.73 开了新架构(newArchEnabled=true)+ Hermes,大概 52-58 帧,偶发掉到 45;
- uni-app 编译成 App(Vue3 + 开启原生渲染),45-55 帧之间反复横跳,滑动停止瞬间会有一次明显卡顿。
但这组数字的参考价值接近于零。因为同一个列表,只要我把那张 300KB 没压过的 PNG 换成 WebP,三者的差距立刻从 15 帧缩到 5 帧以内;再换成 60KB 的 WebP,三个都在 55 帧以上,用户根本感知不到区别。
跨端框架的性能问题里,能归因到框架本身的,我见过的不到三成。剩下七成是图片没压、长列表没做虚拟滚动、setState / setData 一次刷了整个树、在滚动回调里同步做计算。你换个框架,这些坑一个不少,只是报错信息换了个样子。
二、包体积和冷启动:这是唯一能拿去跟老板汇报的硬指标
性能感受主观,包体积是客观的,而且直接跟下载转化率挂钩。我记录过的数字(release 构建、只保留 arm64-v8a,可能随版本浮动,别当标准答案):
- Flutter 空项目 APK 大概 7-8MB,iOS 的 .ipa 解压后 20MB 上下。加上 dio、provider、shared_preferences、flutter_screenutil 这几个常规依赖,Android 直接奔 15MB 去。
- RN 空项目(Hermes 开启、ProGuard 开启、单 ABI)APK 大概 8-10MB,但 JS bundle 跟着业务涨得很快,我们那个老项目打完是 24MB。
- uni-app 用 5+ Runtime 离线打包,基础包 12MB 起步,这个壳是省不掉的;uni-app x 编译成原生会小一些,但生态成熟度还得再等等。
减包体积的具体动作,我按性价比排个序:Flutter 用 flutter build apk --split-per-abi 加 --tree-shake-icons(这个默认就开,但字体没摇过),别引整个 Material Icons;RN 把 enableProguardInReleaseBuilds 打开、abiFilters 只留 arm64、Hermes 一定要开;uni-app 能不用 5+ Runtime 就别用,走 H5 套壳或者小程序。这些动作做完,通常能砍掉 30%-40%。
三、热更新才是真正的分水岭,这点几乎所有对比表都不提
我差点上 Flutter 的那个项目,是做营销活动的工具 App。需求方要求「活动页每天能改文案和价格,不能等审核」。这一句话直接把 Flutter 判了死刑——官方明确不支持代码热更新,Shorebird 这类第三方方案本质是替换 Dart 产物,你新增一个原生插件就失效,而且它自己也在商业化早期。
RN 这边,微软的 CodePush 已经跟着 App Center 在 2024 年 3 月退役了。现在要么自建 code-push-server(GitHub 上有几个开源实现还在维护),要么用 Expo 的 EAS Update——但注意,EAS Update 只能更新 JS bundle,你要是动了原生依赖,照样得走审核。
uni-app 的 wgt 热更新是内置能力,这是它在国内营销类、内部工具类场景里真正的杀手锏,不是性能。DCloud 官方文档里对 wgt 包的限制写得挺清楚,但很多人不看,上来就以为「什么都能热更」。
所以我的判断是:先问「这个 App 一年要发多少次版、能不能等审核」,再问性能。 每周发版的营销型 App,和半年一更的工具型 App,选出来的框架完全可能是两个。
四、招人和生态,这个成本比技术选型贵十倍
我见过最失败的选型决策,是一家二十人的公司选了 Kotlin Multiplatform 做业务层复用,理由是「性能好、原生」。结果招了三个月没招到人,最后那个模块只有当初拍板的那个技术负责人会改,他一离职,整个模块进入冻结状态。
Flutter 和 RN 在国内的招聘市场是有存量的,Dart 和 JS/TS 的候选人池差一个量级。如果你的团队本来就是前端背景,RN 或者 uni-app 的启动成本几乎为零,招个 Vue 或 React 的人两天上手。Flutter 需要重新建立一套 Widget 思维,写惯了 CSS 的人前两周会非常痛苦,尤其是布局约束那套东西。
生态方面还有个容易被忽略的点:鸿蒙 Next。2024 年以后新上架的华为设备不跑 APK,这意味着你的跨端框架必须在 HAP 侧有方案。uni-app 和 RN 系(通过第三方鸿蒙适配)走在前面,Flutter 官方对鸿蒙的支持一直不温不火。这个变量足以推翻很多 2022 年写的选型文档。
五、一个我自己的、可能不太讨喜的观点
大家都在问「哪个框架性能更好」,我觉得这个问法本身就错了。三个框架在中位数场景下的性能都够用,真正的差别是——它们出问题时的表现不一样,而你要选的是那个你能接受的失败模式。
Flutter 挂掉通常是渲染层黑屏或者卡死,Dart 层的错误栈你能看到,但引擎层的崩溃日志看得人头皮发麻;RN 挂掉常见的是 JS 线程崩了导致白屏,但 JS 代码你是能断点调试的;uni-app 挂掉多半是 WebView 白屏,而 WebView 白屏的原因可能有二十种(runtime 版本和 HBuilderX 不匹配、manifest 里 usingComponents 配错、iOS 上 ES6 没转、离线包没更新成功……)。
还有一种情况我越来越想劝退:如果你的 App 只有十来个页面,半年都不改一次,用户量也不大,那你用原生写两遍,很可能比引入任何一个跨端框架都便宜。跨端框架省的是「多端重复开发」的时间,不是「首次开发」的时间。它带来的构建链、调试链、升级链复杂度,对小项目来说是纯成本。
六、几个我确实被问过很多次的具体问题
RN 新架构要不要开? 0.76 之后默认就是新架构了,0.74/0.75 可以在 gradle.properties 里加 newArchEnabled=true 手动开。开了之后 TurboModules 和 Fabric 会替换掉老的桥接,但第三方库没适配的会直接崩,升级前先把依赖过一遍。
Flutter 包体积砍不下来怎么办? 先跑 flutter build apk --analyze-size 看体积构成,很多时候是两个冗余字体文件占了 4MB。别一上来就上资源压缩,先看清楚谁在占地方。
uni-app 白屏怎么查? 安卓连 adb 看 logcat 里的 console 输出,iOS 用 Safari 的 Web Inspector 挂在 WebView 上。90% 的情况是打包时的 runtime 版本和你本地 HBuilderX 版本对不上,重新离线打包一次就好。
写到这里其实也没什么终极答案。我自己的取舍是:需要高频热更、团队是前端背景,选 uni-app 或者 RN;追求 UI 一致性和复杂动效、团队愿意为 Dart 付学习成本,选 Flutter;如果是三五个页面的小工具,老老实实写两遍原生,别折腾。