外观
跨端方案选型:Taro、React Native 与 Flutter
跨端选型最常见的错误,是拿"一套代码跑所有端"当目标。真实项目里,业务形态、性能要求、团队技能和发布渠道共同决定了哪种方案更合适。
这篇文章从实际场景出发,对比 Taro、React Native 和 Flutter 的能力边界,并给出一套可执行的选型方法。
先明确目标端
不同目标端的约束差异很大:
| 目标端 | 核心约束 | 常见方案 |
|---|---|---|
| 微信/支付宝小程序 | 包体积、平台 API、审核 | Taro、原生小程序 |
| iOS / Android App | 性能、原生能力、热更新 | React Native、Flutter、原生 |
| H5 / PC Web | SEO、首屏、浏览器兼容 | React、Vue、Next.js |
| 多端同时覆盖 | 一致性、维护成本 | Taro、Flutter、RN + Web |
如果只需要小程序,原生小程序或 Taro 更直接。如果核心是高性能 App,RN 和 Flutter 更合适。不要为了覆盖一个边缘端,把主端架构拖入不必要的复杂度。
Taro:以小程序为中心的多端编译
Taro 使用 React 或 Vue 语法编写,编译到微信小程序、H5、React Native 等平台。它的优势是贴近小程序生态,适合以小程序为主、同时需要 H5 的场景。
tsx
// src/pages/articles/index.tsx
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import { useState } from 'react'
interface Article {
id: string
title: string
cover: string
}
export default function ArticleList() {
const [articles, setArticles] = useState<Article[]>([])
useLoad(async () => {
const response = await Taro.request<Article[]>({
url: 'https://api.example.com/articles'
})
setArticles(response.data)
})
return (
<View className="article-list">
{articles.map((article) => (
<View key={article.id} className="article-item">
<Image src={article.cover} mode="aspectFill" />
<Text>{article.title}</Text>
</View>
))}
</View>
)
}需要注意:
- 小程序有包体积限制,公共依赖和图片资源要做分包与压缩。
- 各平台 API 能力不同,
Taro.canIUse和条件编译必不可少。 - 样式受小程序限制,复杂的 CSS 特性、选择器和动画不一定可用。
- 编译到 RN 时,运行时差异更大,不能假设 H5 的布局行为一致。
Taro 的定位是"多端编译框架",不是"统一运行时"。跨端差异仍然需要业务代码处理。
React Native:JavaScript 驱动原生渲染
RN 用 JavaScript 描述 UI,最终渲染为原生组件。它适合已有 React 技术栈、需要原生体验和动态发布能力的 App。
tsx
import { FlatList, StyleSheet, Text, View } from 'react-native'
interface Article {
id: string
title: string
}
export function ArticleList({ articles }: { articles: Article[] }) {
return (
<FlatList
data={articles}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.item}>
<Text style={styles.title}>{item.title}</Text>
</View>
)}
initialNumToRender={10}
windowSize={7}
/>
)
}
const styles = StyleSheet.create({
item: {
padding: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#e2e8f0'
},
title: {
fontSize: 16,
color: '#0f172a'
}
})RN 的新架构(Fabric、TurboModules、JSI)减少了旧桥接的通信瓶颈,列表滚动和原生模块调用性能明显提升。但仍然要注意:
- 大量高频 JS 与原生通信仍可能成为瓶颈。
- 列表必须使用虚拟化,
ScrollView渲染长列表会直接卡死。 - 原生模块、权限、推送、深链仍需要原生开发能力。
- 版本升级涉及 React Native、原生依赖和构建工具,升级成本不低。
RN 的最大优势是生态与人才:React 开发者可以快速上手,Web 与 App 可以共享业务逻辑和设计 token。
Flutter:自绘引擎带来一致性
Flutter 用 Dart 编写,通过 Skia/Impeller 自绘 UI,不依赖平台原生控件。它的优势是:
- 多端视觉一致性极高。
- 动画和复杂 UI 性能好。
- 热重载开发体验优秀。
- 单一代码库覆盖 iOS、Android、Web、桌面。
dart
import 'package:flutter/material.dart';
class ArticleList extends StatelessWidget {
final List<Article> articles;
const ArticleList({super.key, required this.articles});
@override
Widget build(BuildContext context) {
return ListView.separated(
itemCount: articles.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final article = articles[index];
return ListTile(
title: Text(article.title),
trailing: const Icon(Icons.chevron_right),
);
},
);
}
}代价是:
- 团队需要学习 Dart 和 Flutter 的组件模型,前端经验迁移有限。
- 自绘引擎意味着包体积更大,平台控件风格需要额外适配。
- 与原生 SDK 集成需要编写 Platform Channel,复杂原生能力仍要原生开发。
- Web 端产物和体验与原生端有差异,不适合作为主要 Web 方案。
Flutter 适合 UI 复杂、设计一致性要求高、团队愿意投入新语言学习的 App 项目。
横向对比
| 维度 | Taro | React Native | Flutter |
|---|---|---|---|
| 主要目标 | 小程序 + H5 | iOS / Android App | 多端 App |
| 语言 | React / Vue + TS | React + TS | Dart |
| 渲染方式 | 编译到各端 | 原生组件 | 自绘引擎 |
| 小程序支持 | 强 | 弱 | 弱 |
| 原生能力 | 依赖平台桥接 | 成熟,需原生开发 | Platform Channel |
| 性能上限 | 受平台限制 | 接近原生 | 很高 |
| 团队迁移成本 | 前端低 | React 团队低 | 需要新技能 |
| 热更新 | 小程序天然支持 | 可做,但受商店政策限制 | 受限 |
组合策略
大型产品往往不是"选一个框架",而是按端组合:
- 小程序 + H5:Taro 或原生小程序 + 独立 H5。
- App + H5:RN 或 Flutter 做 App,Web 继续用 React/Vue。
- 共享层:把 API 客户端、类型定义、业务规则、设计 token 抽成平台无关的包,在多个端复用。
text
packages/
├── api-client/ # 请求与类型,所有端共享
├── business-core/ # 业务规则,纯 TypeScript
├── design-tokens/ # 颜色、间距、字体
├── app-rn/ # React Native
├── app-mini/ # Taro
└── web/ # React / Vue共享 UI 组件要谨慎。不同端的交互模式、布局约束和性能特征不同,强行复用会导致大量条件分支。共享业务逻辑和设计 token 的收益更稳定。
选型清单
选型时按顺序回答:
- 目标端是什么? 小程序、App、Web 的优先级分别是什么?
- 性能瓶颈在哪里? 长列表、复杂动画、大数据量渲染是否需要原生级性能?
- 团队现有能力? React、Vue、Dart、原生开发各自有多少人?
- 是否需要热更新? 小程序和部分 RN 方案支持,Flutter 和原生受限。
- 原生能力依赖多不多? 支付、地图、推送、蓝牙、硬件传感器。
- 长期维护成本? 框架升级、原生 SDK 升级、测试矩阵是否有人负责?
如果答案模糊,先做一个两周的技术验证:用真实业务页面验证性能、原生能力和构建发布流程,而不是只看 demo。
常见坑
- 用 demo 性能做决策。示例页面无法暴露长列表、复杂交互和低端机问题。
- 低估原生开发需求。跨端框架都需要原生能力兜底,团队里必须有能改原生代码的人。
- 强求 UI 完全复用。不同端的交互规范不同,强行统一会牺牲体验。
- 忽略包体积与启动时间。跨端框架的运行时和依赖会显著增加包体积。
- 没有端到端测试矩阵。多端意味着多套设备、系统和版本组合,测试成本必须提前计入。
- 把跨端当长期架构目标。业务早期用原生快速验证,后期再统一,往往比一开始强行跨端更划算。
小结
跨端选型没有统一答案。Taro 贴近小程序生态,React Native 平衡 React 人才与原生体验,Flutter 提供一致的高性能 UI。先明确目标端和性能底线,再结合团队能力做选择;共享业务逻辑和设计 token,比共享所有 UI 更现实。