Skip to content

01 · 跨端方案横向对比:RN / uni-app / Flutter / 小程序 ​

本篇解决什么问题: 你现在做 Flutter,但现实里会不断被问到(或自己纠结): "为什么选 Flutter?" "这个需求用 uni-app 是不是更快?" "老 RN 项目要不要迁?" 本篇给你一套能讲清楚、能站得住脚的判断框架,而不是"我觉得 Flutter 好"。

⚠️ 易变信息说明:各框架的版本号、性能跑分、生态排名变化很快, 按本库约定(正文写原理与判断框架)不写死版本与跑分,请以官方文档为准。 本篇讲架构模型、心智负担、适用边界、迁移成本——这些是长期稳定的。

📌 你的独特视角:HaierEnergy 正是从 uni-app 迁移到 Flutter 的真实案例(见 Flutter 00 篇)。 所以你不是在"学"对比,而是在复盘一次真实迁移。这是本篇最有价值的部分。

相关:Flutter 27 篇(同款选型方法论)、 Flutter 26 篇(混合开发)。


开篇:一张表看懂四条技术路线 ​

React Nativeuni-appFlutter小程序
渲染方式原生组件桥接编译到小程序 + WebView自绘引擎(Skia/Impeller)WebView + 原生混合
语言JS / TSJS / TS(Vue 语法)DartJS / TS
UI 一致性⚠️ 依赖原生组件,两端有差异⚠️ 各端渲染不同✅ 像素级一致✅ 平台内一致
性能中(桥接有损耗,新架构已大幅改善)中低(WebView 为主)✅ 高(自绘,无桥接)中低
包体积中小⚠️ 较大(自带引擎)极小(但受平台限制)
热更新✅ CodePush✅❌ iOS 禁止(见下)✅ 平台支持
前端上手✅ 很友好(React)✅ 最友好(Vue)⚠️ 要学 Dart✅ 友好
主要场景中大型 App、需原生能力多端小应用、国内业务高性能、UI 一致性要求高微信生态、轻量应用

一句话定位:

  • uni-app = 用 Vue 写,一次编译到 10+ 个平台(含各家小程序),主打"覆盖广"
  • RN = 用 React 写,渲染走原生组件,主打"接近原生的体验 + 前端效率"
  • Flutter = 用 Dart 写,自己画每一个像素,主打"性能 + 一致性"
  • 小程序 = 平台内的轻量容器,主打"获客与传播",能力受平台约束

一、架构模型:这是所有差异的根源 ​

理解了渲染方式,其他差异都能推出来。

1.1 三种渲染模型 ​

① WebView 渲染(uni-app 的 H5 端、部分小程序)
   你的代码 → HTML/CSS/JS → WebView 渲染
   └ 优点:跨端最彻底、可热更新
   └ 缺点:性能最差、原生能力弱、体验"不像原生"

② 原生组件桥接(React Native 老架构)
   你的 JS → Bridge(异步 JSON)→ 原生组件 → 系统渲染
   └ 优点:用真正的原生组件,观感原生
   └ 缺点:Bridge 有序列化开销,高频交互/动画会掉帧

③ 自绘引擎(Flutter)
   你的 Dart → Skia/Impeller 直接画到画布 → GPU
   └ 优点:无桥接、性能高、各端像素一致
   └ 缺点:包体积大、不走系统组件(无障碍/输入法等要自己适配)

1.2 为什么 Flutter "一致性"最强 ​

因为它压根不用系统组件——同一个按钮,在 Android 5 和 iOS 17 上画出来完全一样。

一致性代价
RN⚠️ 用 iOS 的 UIButton / Android 的 Button,两端长得不一样,要分别调优但观感"原生"
uni-app⚠️ 编译到不同端,渲染差异更大但覆盖平台多
Flutter✅ 完全一致但系统升级后"不像新系统"(要用 Cupertino/Material 组件模拟)

💡 这是选型的关键权衡: 你要的是"和原生 App 长得一样"(选 RN), 还是"在所有设备上长得一样"(选 Flutter)? 这两个目标是互斥的。

1.3 RN 的新架构(为什么老印象要更新) ​

如果你对 RN 的印象还停留在"Bridge 很慢",需要更新:

老架构新架构
通信Bridge(异步 JSON 序列化)JSI(JS 直接持原生对象引用,同步、无序列化)
渲染多层转换Fabric(渲染更接近同步)
启动全量注册原生模块TurboModules(按需懒加载)

结论:新架构下 RN 的性能已大幅改善,"RN 一定比 Flutter 慢"是过时的说法。 但新架构的迁移本身也有成本(老项目要改造第三方库)。


二、逐方案详解 ​

① uni-app ​

是什么:DCloud 出品,用 Vue 语法写代码,编译到 iOS / Android / H5 / 微信、支付宝、抖音等各家小程序。

维度说明
最大优势一套代码覆盖 10+ 平台,国内小程序生态无人能敌
上手成本✅ 最低(会 Vue 就会写)
性能中低。App 端早期是 WebView,后有 nvue(原生渲染)改善
生态插件市场丰富,但质量参差;深度原生能力仍需自己写插件
坑点各端差异大——"编译通过"不等于"表现一致",条件编译到处都是

条件编译是其最大特色也是最大负担:

js
// #ifdef MP-WEIXIN
wx.login()
// #endif
// #ifdef APP-PLUS
plus.oauth.login()
// #endif

适合:

  • ✅ 需要同时覆盖 App + 多家小程序的业务
  • ✅ 中小型应用、内部工具、营销活动页
  • ✅ 团队是 Vue 技术栈、想快速出东西

不适合:

  • ❌ 高性能要求(复杂动画、长列表、视频处理)
  • ❌ UI 一致性要求高的产品
  • ❌ 重度依赖原生能力的应用

② React Native ​

维度说明
最大优势React 生态 + 原生观感 + 成熟的热更新(CodePush)
上手成本✅ 友好(会 React 即可)
性能中(新架构后已接近原生,复杂场景仍需优化)
原生能力✅ 好(社区库多,TurboModules 规范后更好)
坑点升级** notoriously 痛苦**(版本升级常破坏第三方库);两端 UI 差异要分别调

适合:

  • ✅ 团队是 React 技术栈
  • ✅ 需要热更新(业务快速迭代、绕过发版审核)
  • ✅ 中大型 App,能接受一定的原生开发配合

不适合:

  • ❌ 追求极致性能(游戏、重动画)
  • ❌ 团队没有原生(iOS/Android)支持——RN 迟早要写原生代码

⚠️ 关键认知:RN 不是"不用写原生",而是"少写原生"。 复杂项目里仍需要原生模块、原生调试、原生发版知识。

③ Flutter ​

维度说明
最大优势性能高 + UI 像素级一致 + 单一代码库覆盖移动/Web/桌面
上手成本⚠️ 要学 Dart(但对前端不难,见 Flutter 01 篇)
性能✅ 高(自绘,60/120fps 容易达到)
包体积⚠️ 较大(自带渲染引擎,Release 包通常比 RN/原生大不少)
热更新❌ iOS 明确禁止下发可执行代码(这是硬限制,见 2.4)
坑点包体积、部分系统能力需自己适配(无障碍、输入法、文本选择)

适合:

  • ✅ 高性能、复杂动画、UI 一致性要求高
  • ✅ 品牌类 App(设计要求严格还原、各端必须一致)
  • ✅ 需要同时覆盖移动 + Web + 桌面(Flutter 多平台是真实优势)

不适合:

  • ❌ 强依赖热更新的业务(如频繁改版的活动页、需紧急修复线上 bug)
  • ❌ 极度在意包体积(如极简工具 App、小程序场景)
  • ❌ 重度依赖最新系统特性(自绘引擎要等官方适配)

④ 小程序 ​

维度说明
最大优势微信生态内的获客与传播(扫码即用、社交裂变)
上手成本✅ 低(类 Web 技术栈)
限制⚠️ 能力受平台约束(不能随意跳外链、有审核、包体积上限)
本质不是"跨平台方案",而是平台内的应用容器

关键定位:小程序不是用来替代 App 的,而是获客入口。 常见组合是:小程序做拉新和轻服务,App 做深度留存。


三、决策框架(核心,可直接用) ​

3.1 决策树 ​

开始:你的核心约束是什么?
 │
 ├─ 必须覆盖多家小程序(微信+支付宝+抖音…)?
 │   └─ 是 ──→ uni-app ✅(这个需求几乎只有它能满足)
 │
 ├─ 必须支持热更新(绕过应用商店审核)?
 │   └─ 是 ──→ RN(CodePush)或 uni-app
 │              ⚠️ Flutter 在 iOS 上做不到(见 2.4)
 │
 ├─ 追求极致性能 / 复杂动画 / UI 严格一致?
 │   └─ 是 ──→ Flutter ✅
 │
 ├─ 团队技术栈是?
 │   ├─ React ──→ RN(上手最快)
 │   ├─ Vue   ──→ uni-app 或 接受学习成本的 Flutter
 │   └─ 无强绑定 / 愿意学 Dart ──→ Flutter
 │
 ├─ 需要同时覆盖 移动 + Web + 桌面?
 │   └─ 是 ──→ Flutter ✅(多平台是其独特优势)
 │
 └─ 主要在微信生态内做轻量服务?
     └─ 是 ──→ 小程序

3.2 加权评分表(按需自己打分) ​

不要只看"谁更好",而要按你的项目加权。示例:

维度权重RNuni-appFlutter
性能?中中低高
UI 一致性?中低高
上手成本?高(易)高(最易)低(要学Dart)
多端覆盖(含小程序)?中高中
包体积?中小大
热更新?支持支持❌ iOS 禁止
生态/招人?好中好
长期维护?中中好(Google)

用法:把"权重"填成你项目的真实优先级(1~5),逐项相乘求和。 不同项目得出的结论会完全不同——这才是正确的做法。

3.3 常见组合(真实世界的答案往往不是"二选一") ​

组合场景
小程序 + App小程序拉新,App 做深度留存(最常见)
uni-app 做活动页 + 原生/Flutter 做主 App活动页要快和热更,主 App 要稳
Flutter 为主 + 原生模块主体 Flutter,个别能力(如推送 SDK)用原生
Flutter Add-to-App老 App 逐步引入 Flutter(见 Flutter 26 篇)

四、迁移:这是你最该关心的一节 ​

HaierEnergy 正是从 uni-app 迁移到 Flutter。结合这个真实经历,讲迁移的判断与成本。

4.1 先回答:该不该迁? ​

不要因为"新技术更好"而迁移。 只有以下理由才成立:

✅ 正当理由❌ 不成立的理由
现有方案撑不住性能/体验需求"Flutter 更火"
现有框架停止维护或有致命缺陷"我们想用新技术"
业务形态变了(不再需要覆盖那么多小程序)"领导要求的"
维护成本已经高于迁移成本"重写一遍代码更干净"(几乎总是错觉)

⚠️ 重写是软件工程里最危险的决定之一。 老代码里的"奇怪逻辑"往往对应着某个你不知道的边界情况。

4.2 迁移成本矩阵 ​

从 → 到成本能否增量迁移关键工作
uni-app → Flutter高(几乎重写)⚠️ 难(可用 Add-to-App 分批)全部 UI 重写、语言换 Dart、原生插件重接
RN → Flutter高✅ 可(Add-to-App)UI 与逻辑重写,原生模块可复用
原生 → Flutter中高✅ 可(Add-to-App,最成熟的路径)按页面逐步替换
Flutter → 其他高—同上,反向同理

4.3 三条迁移原则 ​

① 优先增量,绝不 Big Bang

对 1100+ 个 Dart 文件的项目,一次性重写 = 项目延期 + 质量失控。 可行路径是 Add-to-App(见 Flutter 26 篇):

阶段 1:新页面用 Flutter 写,老页面保持原样
阶段 2:把改动最频繁的页面逐个迁移
阶段 3:稳定后考虑整体切换

② 迁移前先补测试

没有测试覆盖的迁移 = 裸奔。至少要有核心链路的冒烟测试 (见 Flutter 19 篇)。

③ 保留"能回滚"的能力

每个阶段结束后都应可独立上线、可回退。不要做"迁一半不能上线"的计划。

4.4 uni-app → Flutter 的特别注意点 ​

基于这个迁移方向,容易踩的坑:

坑说明
条件编译的习惯要改uni-app 里到处 // #ifdef,Flutter 里要用平台判断或抽象接口(策略模式)
Vue 响应式 → Riverpod习惯了 .value / v-model 自动响应,Flutter 里要显式 ref.watch
CSS → Widget 嵌套布局心智完全不同(约束向下、尺寸向上,见 Flutter 04 篇)
小程序 API 要重接wx.xxx 在 Flutter 里没有,要用插件或 Platform Channel
包体积会变大要有心理准备,并做好体积优化(见 Flutter 23 篇)
热更新能力会失去如果业务依赖热修复,要提前规划替代方案(见下)

五、热更新:最容易被忽略的硬约束 ​

这是 Flutter 与 RN/uni-app 最关键的差异之一,选型时务必确认。

方案热更新说明
RN✅ CodePush可下发 JS Bundle,绕过应用商店审核
uni-app✅App 端支持增量更新
Flutter❌ iOS 禁止第三方方案(如 Shorebird)在部分区域/场景可用,但不是官方支持的通用方案

5.1 为什么 iOS 禁止 ​

Apple 开发者协议禁止下发可执行代码(JIT/动态加载代码)。 Flutter 的 AOT 编译产物属于可执行代码,因此官方渠道无法热更新。

⚠️ 网上有些方案(改引擎、动态化框架)处于灰色地带, 有审核被拒的风险。不要在生产项目里赌这个。

5.2 如果你的业务强依赖热更新 ​

替代方案说明
服务端驱动 UI用配置/DSL 描述 UI,Flutter 端解析渲染(可绕过限制,但灵活性有限)
WebView 承载活动页高频变动的页面用 H5,Flutter 里嵌 WebView
加快发版节奏依赖商店审核(iOS 通常较快,但不可控)
重新评估选 RN/uni-app如果热更新是刚需,这就是决定性因素

💡 选型时的一句话检查: "我们能不能接受一个线上 bug 必须等发版才能修?" 不能 → Flutter 可能不合适,或需要 WebView/配置化兜底。


六、如何向别人解释"为什么选 Flutter"(话术) ​

你可能需要在团队里解释这个决定。用"约束 + 权衡"的方式讲,而不是"哪个更好"。

6.1 有效说法(推荐) ​

"我们选 Flutter 主要基于三个约束: ① 设计要求各端严格一致,uni-app/WebView 方案做不到; ② 有复杂图表和动画,WebView 性能撑不住; ③ 后续可能要覆盖桌面端,Flutter 的多平台是现成的。

代价是包体积会变大、iOS 上不能热更新。 包体积我们做优化(按需、拆包),热更新的需求用服务端配置 + WebView 活动页兜底。"

6.2 无效说法(避免) ​

  • ❌ "Flutter 是最好的框架" —— 没有"最好",只有"最合适"
  • ❌ "Flutter 性能碾压 RN" —— 过时说法(RN 新架构已大幅改善)
  • ❌ "Google 出品肯定没问题" —— 诉诸权威,且 Google 也砍过项目

6.3 承认代价反而更有说服力 ​

主动说出代价和应对方案,比只讲优点更专业、也更容易被接受。


七、自检清单 ​

学完本文,你应该能够:

  • [ ] 用一句话说清 RN / uni-app / Flutter / 小程序各自的技术定位
  • [ ] 解释三种渲染模型(WebView / 原生桥接 / 自绘)及其优缺点
  • [ ] 说明为什么 Flutter 的 UI 一致性最强,以及代价是什么
  • [ ] 解释"和原生一样"与"各端一样"是互斥目标,并知道怎么选
  • [ ] 说明 RN 新架构(JSI / Fabric / TurboModules)解决了什么,更新过时的性能印象
  • [ ] 面对一个具体项目,能照着 3.1 决策树给出方案建议
  • [ ] 用加权评分表按项目真实优先级做选型(而不是看"谁更好")
  • [ ] 说出哪些正当理由才该迁移,以及哪些理由不成立
  • [ ] 说出迁移的三条原则(增量 / 先补测试 / 可回滚)
  • [ ] 列出 uni-app → Flutter 的至少 3 个特别注意点
  • [ ] 解释iOS 为什么禁止 Flutter 热更新,以及业务强依赖时的替代方案
  • [ ] 用"约束 + 权衡 + 代价"的方式向他人解释选型(而不是"XX 最好")

八、常见误区 ​

误区正解
"Flutter 性能碾压 RN"⚠️ 过时。RN 新架构(JSI)已大幅改善;复杂场景二者差距没那么绝对
"RN/uni-app 不用写原生代码"❌ 复杂项目迟早要写原生(原生模块、调试、发版)
"uni-app 编译通过就等于能跑"❌ 各端差异大,条件编译到处都是,必须逐端测
"Flutter 也能热更新"❌ iOS 官方渠道禁止下发可执行代码。第三方方案有审核风险
"小程序是跨平台方案"❌ 它是平台内容器,能力与传播都受平台约束
"Flutter 包体积和原生差不多"❌ 明显更大(自带渲染引擎)。要做体积优化,且要有预期
"选一个方案就要用到底"❌ 组合使用才是常态(小程序拉新 + App 留存、Add-to-App 渐进迁移)
"老项目重写一遍会更干净"❌ 重写的风险被严重低估;老代码里的"怪逻辑"常对应未知边界
"Flutter 不用管系统适配"❌ 自绘意味着无障碍、输入法、文本选择等都要自己适配好

九、动手练习 ​

练习 A:给一个真实场景做选型(推荐) ​

假设需求如下,用 3.1 决策树 + 3.2 评分表给出方案,并写三句话说明理由:

"一个电商 App,需要覆盖微信/支付宝/抖音小程序 + iOS/Android App, 有商品列表、详情页、购物车,首页有运营活动页需要每周换, 团队 5 人,都是 Vue 技术栈,没有原生开发经验。"

思考点:

  • 小程序覆盖是硬约束吗?权重多高?
  • 没有原生经验对 RN 意味着什么?
  • 活动页每周换 → 热更新是刚需吗?Flutter 怎么应对?

练习 B:复盘 HaierEnergy 的迁移 ​

结合你自己的经历,回答:

  1. 从 uni-app 迁到 Flutter 的真实驱动因素是什么?(性能?一致性?组织决策?)
  2. 迁移过程中最意外的困难是什么?
  3. 如果重来一次,会在哪个环节做不同的决定?
  4. 现在看,哪些代价是当初没预料到的?

💡 把答案记到 troubleshooting/ 或本文件的批注里—— 这是网上搜不到的、属于你的经验。

练习 C:画一张你自己的决策树 ​

基于你做过的项目类型,画出你个人/团队的选型决策树, 并注明每个判断节点的权重依据(为什么这个约束最重要)。


相关文档 ​

本区 ​

Flutter 区(双向对照) ​