Expo、React Native 与 Flutter:概念、架构、上架与选型

This article is extracted from the chat log with AI. Please identify it with caution.

AI 参与说明(Agent:Codex):本文由 Codex 根据 Expo、React Native、Flutter、Apple、Google Play 与 RevenueCat 的官方公开资料协助整理,并提供面向 iOS 与 Android 的通用架构建议。内容初次整理于 2026-08-10,2026-08-12 复核独立开发者、广告变现小游戏与 In-App Purchases(IAP)的选型建议,2026-08-14 更新 Flutter 3.47 stable 与 Material / Cupertino 独立发包状态,并复核 Expo 与 Apple 的平台关系、OTA 边界及 Expo 的商业进展;产品能力、价格、SDK 版本、商店规则与账号要求可能变化,请以文末一手资料和当前控制台为准。示例使用占位符,不包含账号标识、许可证、签名凭据或项目密钥。

适用范围:本文面向准备从 Web / React 转向移动端、计划同时发布 iOS 和 Android 应用的初学者与团队。这里讨论的是通用产品型 App;游戏、重度 3D、音视频编辑、车机、医疗器械或大量自研原生 SDK 的项目,需要单独做技术验证。

先说结论#

对于已有 React / TypeScript 能力、要做典型 iOS / Android 产品 App 的团队,默认方案应当是:

  • 使用 Expo + React Native + TypeScript 开发一套 iOS / Android 业务代码。
  • 使用 Expo Router 管理页面、深链与认证路由。
  • 开发早期可以用 Expo Go 快速验证,但正式项目应尽早建立 development build、internal preview、store staging 和 production 分层构建链路。
  • 使用 EAS Build / Submit / Update / Workflows 简化签名、云构建、商店上传和兼容更新;EAS 是可选云服务,不会替代 Apple、Google 的开发者账号与审核。

从技术原理看,Expo / React Native 不把 .tsx 翻译成 Swift / Kotlin:Metro 与 Hermes 处理 JavaScript,Fabric 最终驱动 iOS / Android 的 Host View。Flutter 的 Dart 在原生 release 中会 AOT 为机器码,但 Widget 也不翻译成 Swift / Kotlin 或系统控件,而是主要由 Flutter Engine 自行布局和绘制。两者最终都是可签名上架的原生应用包,却不是同一种运行与渲染模型。

上架方面,答案也很明确:

  • 发布 TestFlight 或 App Store 仍需加入 Apple Developer Program。截至 2026-08-10,官方价格是每会员年 99 美元,地区价格可能不同。Apple Developer Program 加入计划
  • 发布 Google Play 仍需 Google Play Developer 账号,注册费为一次性 25 美元。2023-11-13 后创建的新个人账号还要通过 Play Console 手机 App 验证自己可以使用一台真实 Android 设备,并完成至少 12 名测试者连续 14 天的 Closed testing 后申请生产权限;Internal testing 不计入这项门槛。Play Console 注册 设备验证 新个人账号测试要求

Flutter 是成熟且值得考虑的替代方案,但它不能直接运行 React / TypeScript 代码;UI 与业务运行时代码都需要以 Dart 重建。除非团队已有明显的 Dart / Flutter 优势,或产品的核心是高度自绘、跨平台像素一致的图形界面,否则 Expo 的综合成本通常更低。

如果产品包含游戏或 3D 家居能力,也不要简单改写为“Flutter 更适合图形”。Expo 明确可以做 2D 与轻量游戏;单件家具查看 / AR 试摆可以由 Expo 壳调用系统 3D 查看器。复杂空间编辑应给 Unity、ARKit / ARCore 或 RoomPlan 建立独立边界,CAD / BIM 级编辑则需要专用几何内核。此时 Flutter 和 Expo 都只是产品壳,不是 3D / CAD 引擎。

截至 2026 年,两套技术都处于持续高强度迭代和大规模生产使用阶段,不存在一方已经“停止发展”的问题。真正影响项目风险的,是团队语言与交付资产是否匹配、关键原生 SDK 是否可用、以及目标真机的性能验证,而不是框架的热度口号。

想先补齐底层概念?#

本文侧重方案选型与交付边界;如果 Fabric、Hermes、EAS、Widget 或 Engine 等概念还不熟,建议先按下列顺序阅读三篇独立技术文章,再回到本文做方案选择:

  1. React Native 技术原理:从 TypeScript 到原生界面、Fabric 与 Hermes
  2. Expo 技术原理与交付:从 React Native 项目到 EAS 发布
  3. Flutter 技术原理:Dart、Widget、Engine 与原生发布

Expo 到底是什么#

Expo 不是另一套移动操作系统,也不是把网页塞进 WebView。它是建立在 React Native 之上的框架、工具链和可选云服务。Expo 官方目前将其描述为“带云服务的全栈 React Native 框架”;React Native 官方也建议新应用使用框架,并把 Expo 列为当前推荐的社区框架。Expo 首页 React Native:使用框架创建应用

可以把整个体系拆成三层:

  1. React Native 运行层:React、Hermes、Fabric、TurboModules、原生 iOS / Android View,以及必要的 Swift、Objective-C、Kotlin、Java、C++ 代码。
  2. Expo 框架层:Expo SDK、Expo CLI、Expo Router、Expo Modules、app config、Prebuild、config plugins、development build。
  3. EAS 云服务层:Build、Submit、Update、Workflows、Metadata、Hosting、Observe 等服务。
flowchart TB
  A["TypeScript / React 业务代码"] --> B["Expo Router + 应用状态与数据层"]
  B --> C["应用自有 UI / 设计系统"]
  C --> D["React Native 组件 + 选定的 UI 库"]
  D --> E["React Native New Architecture / Hermes / Fabric"]
  E --> F["iOS 原生能力"]
  E --> G["Android 原生能力"]

  H["app config + config plugins + Expo Modules"] --> I["Prebuild / Continuous Native Generation"]
  I --> F
  I --> G

  J["EAS Build / Submit / Update / Workflows"] --> F
  J --> G

React Native 的 New Architecture 以 Fabric、JSI 和 TurboModules 为核心;React Native 0.82 起运行时仅使用该架构。它并不意味着 JavaScript “变成了 Swift 或 Kotlin”,而是让 React、C++ 核心与原生模块之间的交互更直接,也为并发 React 能力和同步原生接口提供基础。New Architecture is here React Native 0.82

.tsx.ipa / .aab:Expo 的技术原理#

直接回答:Expo / React Native 不会把每个 TypeScript / React 组件翻译成等价的 Swift 或 Kotlin 源码。 最终产物的确是 Apple、Google 能签名和安装的原生 App,但业务代码、原生壳与 UI 渲染分别经过不同管线。

构建期发生了什么#

  1. TypeScript 类型在构建时被移除,JSX 被转换为 JavaScript。tsc 主要负责类型检查;Metro 负责解析模块、转换并生成 JavaScript bundle。
  2. 在当前默认的 Hermes 生产配置下,JavaScript bundle 进一步编译为 Hermes bytecode。它由 App 内置的 Hermes JavaScript 引擎执行,不是 ARM 机器码,也不是 Swift / Kotlin。
  3. expo prebuild 根据 Expo SDK 模板、app config、config plugins 与 autolinking 生成或配置 ios/android/ 原生工程。它是在生成“原生工程壳和配置”,不是把业务组件改写成两套原生业务代码。
  4. Xcode / Gradle 分别编译原生入口、React Native / Expo runtime、Swift / Kotlin 模块和第三方原生依赖;Metro / Hermes 产物与图片、字体等资源一起被打进应用。
  5. 概念上,最终 .ipa / .aab 包含:平台原生壳、React Native 与 Hermes runtime、JavaScript bytecode / bundle、静态资源,以及已经编译的 Expo / 第三方 / 自研原生模块。实际文件布局会随平台和构建设置变化。
flowchart TB
  subgraph RN["Expo / React Native"]
    R1["TypeScript / JSX"] --> R2["Metro / Babel 转换与打包"]
    R2 --> R3["Hermes bytecode"]
    R3 --> R4["Hermes 执行 React 业务逻辑"]
    R4 --> R5["Fabric 创建 C++ Shadow Tree"]
    R5 --> R6["Yoga 布局 + Commit / Mount"]
    R6 --> R7["iOS / Android Host Views"]

    RC1["app config + config plugins"] --> RC2["Prebuild 生成原生工程"]
    RC2 --> RC3["Xcode / Gradle 编译原生壳与模块"]
    RC3 --> R7
  end

  subgraph FL["Flutter"]
    F1["Dart + Widgets"] --> F2["开发 JIT / 发布 AOT"]
    F2 --> F3["Widget → Element → RenderObject"]
    F3 --> F4["Layout / Paint / Compositing"]
    F4 --> F5["Flutter Engine / Impeller"]
    F5 --> F6["GPU 绘制像素"]

    FC1["plugins / platform channels / FFI"] --> FC2["Swift / Kotlin / C / C++ 能力"]
    FC2 --> F3
  end

运行期怎样显示一个 <View>#

当前 React Native 渲染管线可以概括为:

  1. Hermes 执行 React 业务逻辑,在 JavaScript 中产生 React Element Tree。
  2. Fabric 为 host component 创建 C++ React Shadow Tree。自定义业务组件会继续归约为 <View><Text><Image> 等 host component,并不会每个都创建原生节点。
  3. Yoga 计算大部分布局;文本等部分尺寸仍需向宿主平台测量。
  4. Commit 后,Fabric 对前后两棵树做 diff,并在 UI thread 的 Mount 阶段创建或更新平台 Host View。Android 可对应 ViewGroupTextView,iOS 对应 UIView 等宿主视图。
  5. Fabric 会做 view flattening,去掉没有必要单独存在的原生 View。因此“React Native 使用原生 View”是对的,但“每个 JSX 节点严格对应一个原生 View”是错的。

JS 调相机、通知、SecureStore 等能力时,实际工作由已经编译进二进制的原生模块完成。New Architecture 使用 JSI、TurboModules 和 Codegen 降低旧 bridge 的序列化与调用开销;Expo Modules API 则让项目用更统一的 Swift / Kotlin API 编写原生模块和原生 View。它们解决的是 JavaScript 与原生能力互操作,不是把 JavaScript 自动翻译为原生语言。React Native render pipeline React Native glossary Hermes Expo CNG Expo Modules API

这也解释了 EAS Update 的边界:同一 native runtime 下可以替换 JavaScript / bytecode 与资源;新增原生模块、权限或 entitlement 时,旧二进制里根本没有对应能力,只能重新走原生构建和商店渠道。

Flutter 从 Dart 到屏幕像素的技术原理#

Flutter 也不会把 Widget 翻译成 Swift / Kotlin 或一棵系统原生控件树。它与 Expo / React Native 的关键区别,是 Dart 的发布编译方式和 UI 的渲染所有权。

编译与打包#

  • 开发阶段,Dart 运行在 VM 中并使用 JIT,因此可以做 stateful hot reload:注入改动后的代码,并尽量保留现有状态。
  • iOS / Android 的 profile、release 构建会把 Dart AOT 编译为目标 CPU 的机器码。这里的“natively compiled”指代码执行形式,不代表 Widget 会变成 UIKit / Android View。
  • Xcode / Gradle 仍会生成标准平台应用包,里面包含平台 embedder / runner、Flutter Engine、AOT Dart 代码、资源和已编译的原生 plugins。Flutter 不是 WebView,也不是只把 Dart 文件原样塞进 App。

Widget 怎样变成像素#

  1. Widget 是不可变的 UI 配置描述;build() 根据状态产生新的 Widget 子树。
  2. Element Tree 保存 Widget 在界面中的长期位置、状态与生命周期,并决定哪些部分需要更新。
  3. RenderObject Tree 负责约束传递、尺寸与位置计算、绘制和命中测试。它是 Element Tree 的子集,不是一棵 UIKit / Android View 树。
  4. Paint 与 compositing 生成场景和图层;Flutter Engine 通过 Impeller 等受支持的渲染路径把场景栅格化并交给 GPU。
  5. iOS 通常由 FlutterViewController 承载 Flutter 渲染表面,Android 通常由 Activity / FlutterView 承载。Material 与 Cupertino Widget 主要是 Flutter 自己的实现,即使外观接近系统组件,也不等于内部使用了真正的 UIKit / Material View。

需要地图、WebView 或特定系统控件时,Flutter 可以用 Platform Views 把原生 View 嵌入 Flutter 画面;调用平台 API 可用 plugins、platform channels / Pigeon,直接调用 C ABI 可用 FFI。Platform View 的合成、手势和性能存在平台权衡,所以它是互操作出口,不是 Flutter 普通 UI 的默认渲染方式。Flutter architectural overview Inside Flutter Flutter Platform Views Flutter platform channels

一张表看懂根本差异#

问题Expo / React NativeFlutter
业务代码发布形态JavaScript bundle;当前默认由 Hermes 编译 / 执行 bytecode原生移动端 release 中,Dart AOT 为目标 CPU 机器码
是否转成 Swift / Kotlin否;Swift / Kotlin 只来自原生壳、模块和平台定制代码否;Dart AOT 机器码也不是 Swift / Kotlin 源码
普通 UI 谁来画Fabric 驱动 iOS / Android Host Views,平台负责这些 View 的绘制Flutter Engine 主要自行布局、合成与栅格化,再交给 GPU
布局模型React Element → C++ Shadow Tree,Yoga 为主,部分测量调用宿主平台Widget → Element → RenderObject,由 Flutter rendering layer 计算
原生能力出口JSI、TurboModules、Native Components、Expo Modulesplugins、platform channels / Pigeon、FFI、Platform Views
热更新体验Metro + Fast Refresh;生产 OTA 可用 EAS Update,但受 native runtime 约束开发期 Dart JIT + stateful hot reload;Flutter SDK 无等价的第一方生产 code push
典型性能风险JS 重计算、无效 re-render、Host View / 列表过多、跨层调用与图片过度 rebuild、layout / paint、shader / raster、Platform View 合成与图片

因此,两者都能生成真正可上架的原生应用包,也都保留各自 runtime。Expo / React Native 更像“用 React 管理平台原生 View”;Flutter 更像“用 Dart 和统一 Engine 自己画 UI”。这比“谁是真原生”更准确,也解释了为什么前者更容易沿用 React / TypeScript 资产,后者更容易实现跨平台像素一致的自绘界面。

初学者最容易混淆的概念#

名称它是什么什么时候使用
React Native用 React 描述移动 UI、调用原生平台能力的框架应用真正的跨平台运行基础
Expo SDK与 Expo SDK 版本配套的一组原生模块和 JavaScript API相机、通知、文件、SQLite、SecureStore、图像等能力
Expo CLI随项目使用的命令行工具,通常通过 npx expo 调用启动 Metro、安装兼容依赖、本地编译、诊断和 Prebuild
MetroReact Native 的 JavaScript / TypeScript bundler开发时打包模块、Fast Refresh,生产时生成 bundle
Expo Router建立在 React Navigation 之上的文件路由方案页面栈、Tab、Modal、深链、Web 路由和认证保护
Expo Go商店里预装了固定原生能力的通用客户端学习、原型和只依赖其已有原生模块的快速验证
Development build通常带有 expo-dev-client、按本项目原生依赖构建的开发版 App正式项目、第三方原生库、自定义配置与团队联调
Prebuild根据 app config 和 config plugins 生成 ios/android/需要本地原生工程或构建原生二进制时
CNGContinuous Native Generation,把原生工程视为可再生成产物的工作流降低双端原生工程升级和配置漂移成本
Config plugin在 Prebuild 阶段以可重复脚本修改原生工程权限、entitlement、Info.plist、Manifest、Gradle 等配置
EASExpo Application Services,可选的 Expo 云服务云构建、签名、上传、OTA 更新与 CI/CD

“Managed workflow / Bare workflow / eject” 是旧语境。今天更实用的理解是:项目可以采用 CNG,也可以自己长期维护 ios/android/;两种方式都能使用 Expo SDK 和大部分 EAS 服务。旧式“eject”已由 npx expo prebuild 和 development build 取代。Expo CNG Expo FAQ

两种原生工程策略必须明确二选一。CNG 项目把原生修改表达为 app config、config plugin 或本地 Expo Module,并允许 ios/android/ 随时重建;手工维护原生工程的项目则应提交这两个目录,并审查每一次生成操作。SDK 57 的 npx expo prebuild 已改为默认清空并重新生成原生目录,--no-clean 才是在现有目录上应用;已有手工修改时不要无审查执行,并确保所有改动已提交或可恢复。Expo SDK 57

Expo 能解决什么,不能解决什么#

Expo 主要解决移动开发中的重复工程成本:

  • 用同一套 app config 管理图标、启动页、URL scheme、权限和常见平台配置。
  • 提供版本配套的原生模块,减少手动挑选相容依赖的工作。
  • 用 Expo Router 建立统一的导航、深链与页面约定。
  • 通过 Prebuild、Autolinking 与 config plugins 自动生成和配置原生工程。
  • 通过 EAS 在 macOS、Windows 或 Linux 上触发 iOS / Android 云构建。
  • 代管或协助生成签名材料,并自动上传 App Store Connect / Google Play。
  • 对与现有原生 runtime 相容的 JavaScript、样式和资源发布 EAS Update。

但 Expo 不会替你解决以下问题:

  • 不会替代 Apple / Google 开发者会员、商店资料、隐私申报、内容分级与审核。
  • 不会自动提供后端、业务建模、鉴权设计或数据合规。
  • 不会让所有第三方原生库天然兼容;缺少 config plugin 时仍可能需要自己编写。
  • 不会保证一份 UI 在 iOS 和 Android 上完全一致,也不应该盲目追求完全一致。
  • 不会让团队永远不碰 Xcode、Android Studio 或原生日志;复杂问题最终仍需理解平台。
  • 不会让任意原生变更通过 OTA 绕过商店审核。

因此,Expo 的价值是把原生工程的“日常默认复杂度”降下来,而不是取消原生平台。

React Native + Expo 可以做游戏吗#

明确可以,但 Expo / React Native 是“应用框架 + 图形、动画和输入能力”,不是 Unity、Godot 或 Unreal 那样的完整游戏引擎。 Expo 官方的 expo-gl 提供 OpenGL ES / WebGL-like 渲染目标,明确可用于 2D 和 3D 图形;Expo 也给出 @shopify/react-native-skia 的版本适配和安装路径。这证明它有实际渲染出口,但不代表 Expo SDK 自带场景编辑器、ECS、物理、导航网格、关卡、资产导入和联机同步等引擎级系统。Expo GLView Expo 中的 React Native Skia React Native Skia 游戏教程

游戏类型Expo / React Native 适合度推荐实现
棋盘、卡牌、答题、文字、解谜、回合制、互动叙事很适合普通 React Native View / Image 处理页面;Gesture Handler 处理输入;Reanimated 处理动效
App 内积分、养成、教育互动或小游戏很适合普通 App 页面负责登录、商城和设置;高频画面交给 Skia / GL 渲染层
精灵、瓦片地图、简单街机、轻量 2D 物理可行,需要专项性能验证优先 Skia Canvas / Atlas,物理、固定时步游戏循环和资产系统由项目补齐
简单 3D 模型展示、旋转、缩放、材质切换可以做 PoCexpo-gl 或经维护性审计的 WebGL 上层库;真机检查资产大小、内存、触摸延迟和老 Android GPU
大型 3D 世界、重度实时物理、高实体数、复杂特效或强确定性联机不建议作为默认首选优先 Unity、Godot、Unreal 或原生引擎;Expo 可作账号、内容、社交和商城外壳

Skia 和 expo-gl 在当前 SDK 参考中都标记为 Included in Expo Go;准确含义是支持对应 Expo SDK 的 Expo Go 二进制已内置它们,不是手机里任意版本的 Expo Go 都能打开新项目。核验本文时,SDK 57 的商店版 Expo Go 仍处于审核过渡:iOS 真机可按官方说明通过 eas go 获取,Android 与 Simulator 可由 Expo CLI 安装,或直接使用 development build。Expo SDK 57 的 Expo Go 说明 正式项目仍应尽早切到 development build,因为 Game Center、购买、广告、控制器、自定义物理库、WebGPU 或嵌入引擎都可能引入 Expo Go 不包含的原生代码。不要在每一帧为每个实体调用 React setState;把帧级状态留在 worklet、Skia / GL 渲染层或原生引擎,只向 React 同步分数、暂停、关卡结果等低频业务状态。React Native 官方性能文档以 60 FPS 为例,每帧只有约 16.67 ms,必须在 release build 和目标真机上评测。Development builds React Native 性能 Skia Atlas

Expo Go、development build 和生产构建怎么选#

建议把交付过程分成五级,并区分“链接直装”与“商店测试轨道”:

阶段产物用途能否提交商店
学习 / 原型Expo Go验证页面、路由和 Expo Go 已包含的原生能力
日常开发Development build运行本项目自己的原生依赖与配置,连接 Metro否,仅用于开发 / 内部评审
链接直装验收Internal previewiOS ad hoc、Android APK,通过内部链接安装否,不是 TestFlight / Play track 产物
商店验收Store staging / release buildTestFlight、Play Internal / Closed,使用最终商店标识与 AAB / IPA
生产Store production buildApp Store / Google Play 审核与分阶段发布

Expo 官方已明确把 Expo Go 定位为学习和快速实验环境。日常开发使用 development build;内部链接评审可以使用 internal distribution;TestFlight、Play 测试轨道与正式发布必须使用 store release build。Development builds 分发评审版本

如果只是先做一个不含自定义原生能力的原型,而且 Expo Go 已支持所选 SDK 与依赖,可以用 npx expo start 获得最快反馈;这不是正式项目必须通过的门槛。只要引入额外原生依赖、原生配置、支付、推送、地图或自定义模块,就应尽早建立 development build。

还要注意版本过渡。整理本文时,Expo 的创建项目页面正处于 SDK 57 过渡期:文档要求显式使用 default@sdk-57,而无模板命令在特定场景仍可能创建旧 SDK 项目。这是临时状态,不应把文章里的 SDK 数字当成永久安装命令。新项目应在创建当天查看官方页面,提交 lockfile,并运行 npx expo-doctor 核验依赖。创建 Expo 项目

推荐的移动端技术栈#

下面是一套偏稳健、不过度堆库的默认组合:

推荐说明
语言TypeScript strict业务、路由、API 契约和组件均使用严格类型
框架Expo + React Native锁定同一 Expo SDK 对应的 React / React Native 版本,不自行混装
JavaScript 引擎Hermes使用 Expo / React Native 默认配置,先测量再调优
路由Expo Routerapp/ 只放路由与 layout,组件和业务代码放到 src/
UI选定的 React Native UI kit组件库解决页面交付效率;复杂组件统一经 src/ui/ 适配后再被 feature 使用
样式语义 token + 选定 UI kit 的样式体系优先统一色彩、间距、文字与深浅色意图;不要让样式工具反过来决定业务架构
动画与手势Reanimated + Gesture Handler先确认当前 Expo SDK 的相容版本;手势、高频动画和列表要在真机测量
远程数据fetch + 应用 API adapter;复杂缓存再加 TanStack Query区分 server state 与本地 UI state,避免一个全局 store 包办一切
本地状态React state / Context 起步只有跨特性、高频共享状态明显增多时再引入专用 store
敏感存储expo-secure-store保存短期令牌或设备侧敏感小数据,不保存后端密钥
结构化离线数据expo-sqlite,按需求启用离线优先、队列和大量结构化缓存才需要,不要为“以后可能”预建复杂同步
测试单元 / 组件测试 + Maestro 关键流程 + 真机验收Overlay、键盘、时区、深链、权限和支付必须覆盖真机
构建发布EAS Build / Submit / Update / Workflowsdevelopment、internal preview、store staging、production 分层;只有 release build 固定 update channel

后端不应由 UI 框架决定。REST、GraphQL、tRPC、BaaS 或自建服务都可以;移动端只依赖稳定的 API 契约、认证协议和错误模型。若已有 Web 后端,应先复用 API schema 与业务类型,再决定是否共享具体 TypeScript 包。

组件库的选型、稳定性、授权依赖与无障碍 PoC,可另见React Native 组件库横评;它与本文的 Expo / Flutter 框架选型保持分离,避免把 UI 库当成移动架构本身。

推荐目录结构#

如果当前只做 iOS 和 Android,先使用单一 Expo App;不要在项目还没有真实跨应用共享需求时引入 monorepo。

app/
├── _layout.tsx
├── +not-found.tsx
├── (public)/
│   └── sign-in.tsx
└── (app)/
    ├── _layout.tsx
    ├── (tabs)/
    │   ├── _layout.tsx
    │   ├── index.tsx
    │   └── settings.tsx
    └── item/
        └── [id].tsx

src/
├── features/
│   ├── auth/
│   └── items/
├── ui/
│   ├── components/
│   └── adapters/
├── services/
│   ├── client.ts
│   └── errors.ts
├── design/
│   ├── tokens.ts
│   └── theme.ts
├── storage/
├── config/
└── test/

assets/
global.css
app.config.ts
eas.json

app/ 只负责路由入口和 layout。业务组件、请求、类型、存储和设计系统不与路由文件混放。这样可以在不改 URL 结构的情况下重构 feature,也能让平台特化文件使用 .ios.tsx.android.tsx.native.tsx

如果未来确实出现多个应用、共享的 API 契约或领域逻辑,再建立 packages/core;在此之前,让单一 App 的路由、feature、服务和平台适配边界保持清晰,比预先抽象跨端 UI 更重要。

应用配置与四层交付环境#

下面的 app.json 是可以运行的最小示例;真实项目还应补图标、启动页、权限文案和关联域名。Bundle ID / package name 一旦进入商店就应视为稳定标识。

{
  "expo": {
    "name": "Example",
    "slug": "example",
    "version": "1.0.0",
    "scheme": "example",
    "runtimeVersion": {
      "policy": "appVersion"
    },
    "ios": {
      "bundleIdentifier": "com.example.product"
    },
    "android": {
      "package": "com.example.product"
    }
  }
}

appVersion 是 EAS Update 部署指南当前的默认建议:runtime version 取自 expo.version。只要升级 Expo SDK、增删原生依赖、修改权限或其他原生配置,就必须提升 expo.version 并重新构建,不能只增加 iOS build number / Android versionCode。fingerprint 可以更保守地按项目内容计算兼容性,但当前仍属实验性选择、并非面向所有项目的默认推荐;团队做过构建耗时与兼容性评估后再采用。EAS Update deployment Runtime versions

eas.json 把日常开发、链接直装、商店验收和生产隔离。development build 不绑定 update channel;internal preview 是可更新的 release build;staging 与 production 则使用最终商店标识和相同原生依赖 / 配置基线,但分别接收 stagingproduction channel:

{
  "cli": {
    "appVersionSource": "remote"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "environment": "development"
    },
    "preview": {
      "distribution": "internal",
      "environment": "preview",
      "channel": "preview"
    },
    "staging": {
      "environment": "production",
      "autoIncrement": true,
      "channel": "staging"
    },
    "production": {
      "environment": "production",
      "autoIncrement": true,
      "channel": "production"
    }
  },
  "submit": {
    "staging": {
      "android": {
        "track": "alpha"
      }
    },
    "production": {}
  }
}

初始化和构建命令:

npx expo install expo-dev-client
npx eas-cli@latest init
npx eas-cli@latest build:configure
npx eas-cli@latest update:configure

# 日常开发构建
npx eas-cli@latest build --platform all --profile development

# 链接直装验收
npx eas-cli@latest build --platform all --profile preview

# TestFlight / Play Closed 的商店验收构建
npx eas-cli@latest build --platform all --profile staging

# 上传刚完成的 staging 产物;Android alpha 即 Closed testing 轨道
npx eas-cli@latest submit --platform ios --profile staging
npx eas-cli@latest submit --platform android --profile staging

# 发布一条与 preview build runtime 相容的 JS / asset Update
npx eas-cli@latest update \
  --channel preview \
  --environment preview \
  --message "preview verification"

# 生产商店构建
npx eas-cli@latest build --platform all --profile production

# 上传最近构建;CLI 仍会要求选择产物或补足商店配置
npx eas-cli@latest submit --platform ios --profile production
npx eas-cli@latest submit --platform android --profile production

固定的 app.json 示例让所有 profile 共用同一 bundle identifier / package name,因此不同构建会互相覆盖。如果要在一台设备并行安装开发、preview 与正式 App,应改用 app.config.ts 为前两者增加 .dev / .preview 后缀;staging 与 production 必须继续共用最终商店标识。在本文的 appVersion 策略下,bundle identifier / package name 不参与 runtimeVersion 计算,所以换标识不会自动防止串更。Update 路由要用 EAS project / update URL 与 channel 隔离,发布时还要使用正确的 EAS environment;如果 variant 的原生层不同,还必须让每个不兼容 variant 得到不同的兼容 runtime,例如在 app.config.ts 中按 variant 设置不同 expo.version / runtimeVersion,或在评估后采用 fingerprintApp variants Runtime versions

EAS Submit 只负责把 .ipa / .aab 上传到 App Store Connect 或 Google Play 对应轨道,不等于已经提交审核,更不等于对公众发布。截图、metadata、协议、Data safety / 隐私资料、审核账号、选择 build、App Review 与分阶段 release 仍需在商店侧完成。Submit to app stores

任何私有 npm registry、UI 服务或第三方原生 SDK 的安装 token,都应只在 EAS Dashboard 或 CI secret store 中以 secret 可见性提供;EXPO_PUBLIC_* 会被内联到客户端 JavaScript 中,任何用户都可能读取,绝不能存放安装 token、数据库密码、服务账号 JSON、Apple 私钥或后端 API secret。Expo 环境变量

数据、认证与安全边界#

一个可维护的移动端数据层至少应区分三类状态:

  1. 远程状态:API 返回的数据、分页、缓存、重试和失效。简单项目使用 fetch + adapter 即可;缓存关系复杂时再引入 TanStack Query。
  2. 本地 UI 状态:当前 Tab、弹窗、筛选条件和未提交表单,优先保留在组件或 feature 内。
  3. 持久化状态:令牌、设置和离线数据。敏感小数据进入 SecureStore;大量结构化数据进入 SQLite;普通缓存也要设过期与迁移策略。

移动客户端不是可信环境。即使变量名没有 EXPO_PUBLIC_,只要秘密最终被打进 App,也可能被提取。正确做法是:

  • OAuth / OIDC 移动端使用适合 public client 的流程,例如 Authorization Code + PKCE。
  • access token 尽量短期,refresh token 按威胁模型存入系统安全存储。
  • 支付私钥、第三方服务 secret、管理员操作和签名逻辑全部留在后端。
  • 日志、崩溃报告和埋点在发送前脱敏,不记录完整 token、密码和敏感业务内容。
  • 所有服务器权限都在服务端重新校验,不能信任客户端传来的角色或价格。

EAS Update 能更新什么#

可以通过 EAS Update 发送的通常是:

  • JavaScript / TypeScript 逻辑。
  • 样式、文案和随 bundle 发布的静态资源。
  • 与已安装二进制中的原生 runtime 相容的修复。

需要重新构建并走商店或测试渠道的通常是:

  • 新增、删除或升级会改变原生代码的包。
  • 修改权限、entitlement、原生 target、Info.plist、AndroidManifest 或 Gradle 配置。
  • 升级 Expo SDK / React Native,或改变原生模块接口。
  • 任何改变 native runtime 的内容;使用 appVersion 策略时要提升 expo.version

runtimeVersion 的作用是阻止不相容 Update 进入旧二进制,而不是绕开 Apple 或 Google 的政策。生产更新应先进入与 production 使用相同商店标识、appVersion 和原生配置基线的 staging build / channel,再做小比例 rollout,并保留回滚路径。EAS Update Runtime versions

回滚 Update 只会让客户端切回另一份 JavaScript / 资源,不会自动撤销已经执行的 SQLite / 文件迁移、服务端写入、支付动作或其他副作用。持久化 schema 应使用可前后兼容的 expand / contract migration,在旧、新 bundle 都不再运行前不要立刻删除旧字段;不能把 EAS rollback 当成数据库事务回滚。EAS Update overrides

推荐发布流水线#

flowchart LR
  A["Pull Request"] --> B["lint / typecheck / unit tests"]
  B --> C["Development build 或 preview update"]
  C --> D["iOS 与 Android 真机验收"]
  D --> E["Store staging build"]
  E --> F["TestFlight / Play Closed 验收"]
  F --> G["Production build"]
  G --> H["TestFlight / Play 最终 smoke test"]
  H --> I["App Review / Production rollout"]
  I --> J["分阶段发布与监控"]

生产门禁至少包括:

  • TypeScript、lint、单元和组件测试通过。
  • iOS / Android 的启动、登录、深链、离线恢复、推送、键盘和返回键通过真机检查。
  • 弹层、日期时区、深色模式、字体缩放、reduced motion 和安全区通过回归。
  • Store staging 与 production 使用相同最终商店标识、appVersion 和原生依赖 / 配置基线;internal preview 可以是独立 variant,不能冒充最终验收环境。
  • staging 产物先上传 TestFlight 与 Play Closed testing 验收,最终 production 产物仍要在测试轨道做 smoke test,再提审或提升到生产。
  • 生产采用 staged rollout,并观察崩溃、启动、接口失败率和关键业务指标。

上架账号与费用#

场景Apple / Google 会员Expo 账号备注
Expo Go 学习不需要付费会员不一定需要直接使用商店中的 Expo Go
iOS Simulator 本地开发不需要付费会员不需要本地编译需要 macOS 和 Xcode
免费 Apple Account 安装到自己的 iPhone可通过 Xcode 本地签名不需要Expo 文档说明这是无付费会员安装自有 development build 的路径,能力与有效期受免费签名限制
EAS iOS 真机分发 / TestFlight / App Store需要 Apple Developer Program使用 EAS 时需要99 美元 / 会员年,组织账号通常需 D-U-N-S Number
Android 本地 APK不需要 Google Play 会员本地构建不需要可直接安装到设备或模拟器
Google Play 测试与生产需要 Play Developer 账号使用 EAS 时需要一次性 25 美元;新个人账号另有真实设备验证、Closed testing 与生产权限申请门槛

EAS 可以帮助生成、托管和使用签名凭据,但账号所有权、法律协议、税务、商店资料和审核责任仍属于开发者或组织。Expo 商店构建 EAS Submit iOS EAS Submit Android

账号类型最好在创建商店记录前确定。Apple 个人会员上架时显示个人法定姓名;组织会员显示法人实体名称,通常需要 D‑U‑N‑S Number、组织域名邮箱和网站。Google Play 也会验证开发者身份与联系信息。对 2023-11-13 后创建的新个人账号,还要用 Play Console 手机 App 验证可以使用真实 Android 设备;只有 Closed testing 才计入“12 人连续 14 天”要求,Internal testing 不计入,达标后仍要申请 production access。EAS 不会隐藏或替换商店公开的卖方身份。Apple membership comparison Apple enrollment Play Console registration Play 设备验证 Play 封闭测试要求

2026 年的商店工具链也有明确时限:Apple 自 2026-04-28 起要求上传产物使用 Xcode 26 与 iOS 26 SDK 或更新版本构建;Google Play 对手机和平板的新 App 与更新自 2026-08-31 起要求 target Android 16 / API 36。Expo SDK 57 当前的版本矩阵满足这组基线,但要求会继续滚动,准备提交时仍应重新检查官方动态页。Apple upcoming requirements Google Play target API requirements

是否一定要 Mac#

  • Expo Go、Android 开发和 EAS 云构建可以从 Windows、Linux 或 macOS 发起。
  • EAS Build / Submit 可以在非 macOS 系统上生成和上传 iOS 产物。
  • 若要本地运行 iOS Simulator、本地编译、使用 Xcode 调试或手动上传,则仍需 macOS 和 Xcode。
  • 无论从什么系统发起,TestFlight / App Store 都仍需付费 Apple Developer Program。

如果只做 iOS,Expo 能否使用 CloudKit#

可以。 Expo 生成的 iOS 应用是能链接 Apple 原生 framework 的 React Native App,因此可以在 Swift 中使用 CloudKitCKContainerCKDatabaseCKRecord。但截至 2026-08-10,Expo SDK 当前包索引中没有第一方 CloudKit JavaScript 模块;完整 CloudKit 不能在 Expo Go 里凭一个 JS import 启用。它需要原生模块、iCloud capability、entitlement、container 和重新构建的 development / release build。Expo SDK 参考 Expo Modules Apple:启用 CloudKit

先区分两类完全不同的需求:

需求Expo 接入方式能力边界
让用户通过系统文件选择器选择 / 导入 iCloud Drive 文件expo-document-pickerios.usesIcloudStorage: truegetDocumentAsync 是选择 / 导入流程,默认把选中文件复制到 App cache;写回 / 导出需要另外实现。这仍不是 CloudKit record database API。Expo DocumentPicker
使用私有、公开或共享数据库,读写 CKRecord 或处理 CloudKit 同步用维护活跃且已验证 New Architecture 的 React Native 模块,或自建 Swift 本地 Expo Module必须使用 development build / 正式二进制;修改原生代码、entitlement 或 container 后都要重新 build

对只做 iOS 的项目,我更推荐把 CloudKit 封装成项目自有的本地 Expo Module,而不是让 feature 页面直接依赖某个第三方 bridge:

src/features/*
  → src/services/cloud/index.ts
  → src/services/cloud/cloudkit.ios.ts
  → modules/cloudkit/ios/*.swift
  → CKContainer / CloudKit

使用 npx create-expo-module@latest --local 创建本地模块,用 Swift 只暴露应用真正需要的少量、强类型异步方法,例如账号状态、查询、保存、删除和订阅。业务层仅依赖 TypeScript interface,日后更换 backend 或增加 Android 时,可以新增 adapter,不用重写页面。Expo Modules 入门

CloudKit 的 capability 配置大致如下。这个片段只表达 entitlement 意图,不是完整 CloudKit adapter;实际 container 名、Push Notifications 需求和 config plugin 必须按模块实现核对:

{
  "expo": {
    "platforms": ["ios"],
    "ios": {
      "bundleIdentifier": "com.example.product",
      "entitlements": {
        "com.apple.developer.icloud-container-identifiers": [
          "iCloud.com.example.product"
        ],
        "com.apple.developer.icloud-services": ["CloudKit"]
      }
    }
  }
}

EAS Build 会根据 introspected app config 同步它支持的 Apple capability,iCloud 在支持列表中;CloudKit Container 也可以自动注册并分配,但由于 App Store Connect API 不支持这类操作,自动化步骤需要在本地进行 Apple cookies 身份验证。本地 entitlement 应作为配置事实源:如果远程 App ID 已开启 capability,但本地配置中不再存在对应 entitlement,EAS 后续同步可能会将远程能力关闭。构建前可用下面的命令检查最终 entitlement,构建后还要在 Apple Developer Console 核对 App ID、container 和 provisioning profile:

npx expo config --type introspect

启用 CloudKit 按 Apple 当前流程需要有效的 Apple Developer Program 会员资格,并由 Account Holder 或 Admin 配置;免费 Personal Team 不是 CloudKit 开发路径。这个开发者团队身份用于配置 App ID、container、entitlement 和 provisioning,而设备上的 iCloud 用户账号负责实际数据访问,两者角色不同。CloudKit container 创建后不能删除或重命名,因此要在确定 bundle identifier、Apple Team 归属和 dev / preview variant 的 container 共享策略后再创建。Apple iOS capability matrix Apple:启用 App capability

CloudKit 分 development 与 production environment。这与 EAS build profile 的 environment 和 EAS Update channel 无关;CloudKit 环境由签名产物中的 com.apple.developer.icloud-container-environment 与 provisioning profile 决定。Simulator 可用于 development 环境,TestFlight 与 App Store 只使用 production,production 验收要在真机上进行。EAS Build 不会替你把 CloudKit schema 部署到 production,schema promotion 也不会复制 development 测试数据;生产 schema 的变更应设计为向前、增量兼容。上架前要用真实 iCloud 账号、多台真机和 TestFlight 检查首次登录、无 iCloud 账号、断网、冲突、配额与生产 schema。Expo iOS capabilities Apple iCloud services entitlement Apple CloudKit 配置 Apple CloudKit schema workflow

选型上,如果产品确定只服务 Apple 生态、数据主要属于用户个人且希望使用 iCloud 同步,CloudKit 是合理选择。如果未来很可能增加 Android / Web、非 Apple 账号、复杂后台管理、跨平台分析或服务端工作流,优先使用平台中立 backend。CloudKit JS / Web Services 确实提供非原生访问路径,但它不会让 Android 直接具有 iOS CKContainer 的同等接入体验,也不应被当成无成本的跨平台后端。CloudKit JS

Expo 与 Apple 的关系#

最准确的表述是:Expo 是 Apple 平台上的第三方 React Native 框架与可选交付服务商,不是 Apple 的产品或 App Store 审核代理。 公开资料没有显示 Apple 对 Expo 存在所有权、独占合作关系或审核豁免。Apple 规则审查的是以某个开发者主体提交的最终 iOS App、其元数据、后台服务与商业行为;Expo 负责 SDK、工具链,以及可选的 EAS Build / Submit / Update 等服务。

这也是为什么“Expo 在 Android 上能运行”不能直接推出“它在 iOS 上有特殊通道”。一个正式 Expo iOS App 仍是由项目所属 Apple Team 签名、以自己的 Bundle ID 上传到 App Store Connect 的 .ipa。使用 EAS 创建用于 App Store 的构建时,项目仍需要 Apple Developer Program 会员和能够处理证书、Identifier、provisioning profile 的 Apple 账号权限;EAS 可以在获授权时协助生成、保管或使用这些凭据,但不会取得 Apple Developer Program 的主体资格,也不能替开发者接受协议或承担审核责任。Apple 的协议允许第三方 Service Provider 协助使用开发工具,却明确要求 App Store / TestFlight 的提交主体仍是开发者本人,且开发者须对服务商行为负责;因此 EAS Submit 的自动化上传不等于 Expo 替你发起 App Review。Expo:Apple Developer Program roles and permissions for EAS Build Expo:创建 App Store 构建 Expo:EAS Submit Apple Developer Program License Agreement

从工程风险角度,应把 Apple Team、Bundle ID、App Store Connect app record、支付协议和生产签名凭据视为项目/客户自己的资产;EAS 是可替换的构建与发布基础设施。这样即使以后改为本地 Xcode、其他 CI 或 bare React Native 路线,App Store 的主体与既有用户安装包仍由项目团队控制。

Apple 对 React Native、Expo 或 Flutter 有额外审核限制吗#

没有针对这三个框架的公开禁令,也没有“App 必须用 Swift / SwiftUI 编写”的规则。 这是根据 2026-08-10 现行规则得出的结论,不代表 Apple 对任何框架的认证,也不保证个别 App 通过。Apple 审核最终 .ipa 的公开 API 使用、完整性、体验、内容、隐私和商业行为,而不是组件源码用 React 还是 Dart 描述。Apple 的当前常用第三方 SDK 清单甚至明确列出 FlutterhermesUnityFramework;这意味着在 Apple 规定的提交场景下需要 SDK privacy manifest,作为二进制依赖使用时还需要 SDK signature,并不是禁用或背书。App Review Guidelines Apple 第三方 SDK 要求

审核点对 Expo / React Native / Flutter 的实际含义
构建与签名基线自 2026-04-28 起,上传 App Store Connect 的 iOS / iPadOS App 必须使用 Xcode 26 或更高版本、iOS / iPadOS 26 SDK 或更高版本构建。Expo / EAS 并不能绕过该门槛;选择 Expo SDK、EAS build image 或本机 Xcode 时都要确认它们已支持当前 Apple 要求。Apple:SDK minimum requirements
公开 API、完整性与性能只使用公开 API,提交最终、真机测试过、后端可用的版本;崩溃、占位内容、明显卡顿、过度发热和无法访问的审核路径都会造成问题,与框架无关
Minimum Functionality / 模板4.2 要求 App 超越重新打包的网站,具备足够效用或娱乐价值。使用任何 UI kit 或模板本身不违规;只换 Logo / 内容就用多个 Bundle ID 批量上架的白标克隆,才是 4.2.6 / 4.3 的主要风险
EAS Update / OTA这里要同时读 App Review 2.5.2 和 Apple Developer Program License Agreement 3.3.1(B)–(C):前者禁止通过下载代码引入或改变 App 功能;后者明确允许受限的 downloaded interpreted code,但它不得使 App 的主用途偏离审核时的宣传、不得绕过签名 / sandbox / 安全机制,也不得形成其他 App 的店铺;此外不得经 App Store、Custom App Distribution 或 TestFlight 之外的机制解锁新功能。EAS Update 的技术边界是兼容 native runtime 内的 JS、样式和 assets,不是 Expo 特有的政策豁免。实践中应将 OTA 保守用于已审核用途内的修复和小幅实现改进;原生依赖、权限、entitlement,以及主要用途、支付、隐私或其他实质功能变化,应出新二进制并重新提交审核。不得用 OTA 激活审核时隐藏的功能、外部支付或代码 / 插件商店。App Review Guidelines Apple Developer Program License Agreement EAS Update
Privacy Manifest 与权限PrivacyInfo.xcprivacy、Info.plist 权限用途文案、App Store Connect Privacy Nutrition Label 和 App 内 / metadata 的隐私政策是四件不能互相替代的事。Xcode 会聚合 SDK manifest,但开发者仍对直接与传递依赖、required reason API 和最终隐私申报负责。Privacy manifest App Privacy Details
登录与账号删除使用 Google、Facebook、微信等第三方 / 社交登录建立 primary account 时,4.8 还要求提供符合它的等价登录选项,实践中通常是 Sign in with Apple,规则也列有例外。只要 App 支持创建账号,就必须让用户在 App 内发起删除完整账号,不能只停用账号。Apple 账号删除要求
支付与游戏App 内消费或解锁的数字功能、内容、订阅、关卡或虚拟币的基线规则是使用 In-App Purchase;实体商品或在 App 外消费的现实服务使用其他支付。美国 storefront 目前对外部购买链接 / 按钮有不同规则,EU 等地区还可在特定协议下使用 alternative distribution 或支付选项;它们不是全球通用的“绕过 IAP”许可。区域、reader、enterprise、multiplatform 等例外会变,应按实际上架 storefront 与已接受协议重新核对;随机虚拟物品还需在购买前披露概率。App Review Guidelines 3.1 Apple:EU app distribution
AR / 3D 家居4.2.1 明确说使用 ARKit 时应提供丰富、紧密集成的 AR 体验;只把一个模型放进 AR View 或重放动画不足以构成完整价值。尺寸、摆放、保存方案、商品配置或房间扫描等应与主业务真正结合
审核访问登录型 App 提供有效 demo account 或完整 demo mode,保持 backend 可访问,并在 Review Notes 里说清非显而易见的原生能力、订阅、游戏购买和 OTA 机制

App Review Guidelines 是持续更新的 living document,区域支付规则和第三方 SDK 清单尤其容易变化。每次提交前都应重新阅读英文当前版和开发者账号里已接受的协议,不要把某次成功上架当成永久先例。

Expo Go 是什么,EAS 是否必须付费#

Expo Go 不是付费套餐,也不单独收费;它是 Expo 官方预先发布的通用开发 App。 它的 native runtime 是固定的,适合学习、原型和验证它已经内置的 Expo SDK 能力;它不能按项目需要动态加入原生库、entitlement 或自定义 Swift / Kotlin 代码。

你在定价页看到的 Free、Starter、Production、Enterprise(若你看到的是“Start”,正式名称是 Starter)则是 EAS 云服务套餐,和 Expo Go 是两件事:

名称实际是什么是否需要付费
Expo Go预装固定原生能力的开发客户端不是 EAS plan;学习使用不要求购买 EAS
development build你的项目专属开发 App,通常含项目原生依赖与 expo-dev-client不是 plan;可本地构建,也可使用 EAS Build 的任意可用配额构建
EAS Free / Starter / Production / EnterpriseBuild、Update、Workflows 等云服务的计费档位Free 为 $0;截至整理日 Starter 为 $19/月 + 用量,Production 为 $199/月 + 用量,价格与额度会变化
build.productionbuild profile key;省略 --profile 时 EAS CLI 会查找这个默认名称profile key 可自定义,不等于购买了 EAS Production plan
channel: "production" / environment: "production"分别是 Update channel 与 EAS 环境变量集合都与 build profile 和付费 plan 独立;默认环境为 development / preview / production,自定义环境的权限取决于套餐

Expo 框架、SDK 和 CLI 是开源的;应用也可本地或用其他 CI 构建。EAS Free 目前提供低优先级的 Build queue,以及有限的 Build、Update、Workflows 等服务配额;需要超过 Free 包含额度的容量时,再按所选付费套餐与用量条款购买。Apple / Google 开发者会员费又是完全独立的费用。详细工作流见Expo 技术原理与交付;具体计划以 EAS pricing 为准。

Expo 的商业模式与商业发展#

Expo 的商业逻辑不是出售 React Native 许可证,而是以开源框架降低采用门槛,再把持续的构建、交付与运维工作流做成可计费的云服务。可以把它理解为“开源入口 + 移动 DevOps SaaS”,而不是 Expo Go 的单一产品:

层级面向用户的内容可核查的商业化方式
框架层Expo SDK、CLI、Router、Modules、CNG;可在本地或其他 CI 中使用免费开源工具带来开发者采用;不是强制购买 EAS 的许可
托管基础设施EAS Build、Update、Submit、Workflows、Hosting、Observe 等订阅加用量计费:Build credit / 并发、OTA 的 MAU / 带宽,以及 CI、Hosting 等资源;当前 Starter 为 $19/月 + 用量、Production 为 $199/月 + 用量、Enterprise 为定制合同,价格和额度会调整。EAS pricing 订阅与用量说明
企业服务团队治理、凭据管理、SSO、SLA、支持与合规资料Enterprise 计划和支持合同。EAS 已发布 SOC 2 Type 2 合规信息,这降低大型组织采购门槛,但不等于客户可免做自身的安全、隐私与供应商审查。Expo:SOC 2 Type 2

截至 2026-08-14,最强的公开经营信号是 Expo 在 2026-04-16 宣布完成由 Georgian 领投的 4,500 万美元 Series B,并称资金用于加快产品和招聘。联合创始人 Charlie Cheever 同篇文章表示公司“已盈利一段时间”;这是公司自己的表述,未公开审计口径、利润金额或期间定义。Expo Series B 公告 公司新闻稿

产品面也显示其正从“开发框架 + 云构建”延伸到完整交付平台:2025 年推出 EAS Workflows,Expo 当时称已有接近 1,000 个 App 在生产中使用;2026 年又推出仍处 beta / early-access 的 Expo Agent,并继续扩展可观测与托管能力。这些是持续投入和潜在增长的信号,而不是已公开的收入证明。EAS Workflows Expo Agent beta

因此,谨慎结论是:Expo 已有清晰的付费 SaaS 模式、企业化能力、生产使用案例与新一轮融资,商业化不是停留在 Expo Go 的设想;但它仍是私营公司。 年收入、ARR、付费客户数、流失率、估值、现金储备和利润规模都没有可核查的公司级公开披露,不能仅凭融资、GitHub star 或官网案例推算它们。对项目选型更实际的缓释办法,是保留本地 / 自建 CI 构建路径,并让 Apple / Google 的账号、签名和商店资产始终归项目团队所有。

2026 年的迭代状态与市场响应#

本节是 2026-08-10 的时间切片。版本号、下载量、开发者规模和 GitHub 数据都会变化;其中“开发者规模”“市场排名”等数字来自项目方公开口径,不应当作独立审计的市场份额。

迭代状态:都很活跃,但升级节奏不同#

维度Expo / React NativeFlutter
当前生产基线npm latest 为 Expo 57.0.11;SDK 57 对齐 React Native 0.86、React 19.2.3,最低 Node.js 22.13.x、iOS 16.4、Android 7 / API 24,compile / target 36、Xcode 26.4+。React Native latest 为 0.86.2,0.87 仍是 RC官方 release metadata 当前 stable 为 Flutter 3.47.0、Dart 3.13.0(2026-08-12 发布)
稳定发布节奏Expo 过去多年约每年 3 个 SDK,并在稳定版之间提供 beta / canary;SDK 57 正在探索穿插更小、更接近 React Native 的可选升级,但这还不是永久承诺。React Native 当前按每年约 6 个 minor 的 release train 推进2026 年计划 3.41、3.44、3.47、3.50 四个 stable release target;3.47 已按 August target 发布,另有通常按月发布的 beta 和持续 patch
2026 年主要方向React Native 0.82、Expo SDK 55 起已不再支持 Legacy Architecture;Hermes V1 在 SDK 55 可选,到 SDK 56 / React Native 0.85 才成为默认。Expo 持续推进 Expo Modules、CNG、Router、原生 UI、预编译构建及 EAS 的 Build / Update / Workflows / Observe推进 Android 上的 Impeller、Web Wasm、Android / iOS 新版本适配;3.44 已加入 Hybrid Composition++、默认 Swift Package Manager,并冻结 core Material / Cupertino,3.47 时官方独立 material_ui / cupertino_ui 1.0.0 已发布;Agentic Hot Reload、GenUI 等仍混有实验或预览能力
成熟度判断移动端主链路成熟,Expo 已不再是早期“受限 managed workflow”的产品;但 SDK 与 React Native 绑定,升级必须按 Expo SDK 整体核验移动端主链路成熟,稳定 channel 适合生产;SDK 集成度较高,但 Flutter / Dart / engine / plugin 的升级仍需整体验证
当前工程风险SDK 57 正处创建模板与 Expo Go 的过渡期;第三方原生库、config plugin 和 New Architecture 相容性仍要测试。SDK 57 changelog 还记录了 Hermes V1 与 Reanimated 导入导致内存增加的已知回归平台 plugin、Platform View、渲染后端切换和各端视觉语义仍可能带来升级回归

来源:Expo SDK 55 Expo SDK 57 Expo SDK 版本矩阵与发布策略 Expo npm registry React Native 0.82 React Native 当前版本 React Native npm registry Flutter SDK archive 与 2026 release windows Flutter 官方 release metadata Flutter 3.44 material_ui cupertino_ui Flutter / Dart 2026 roadmap

这里最容易犯的错误,是只看“最新版本号”判断活跃度。Expo 的版本号较小,是因为它使用 SDK 序号并跟随 React Native;Flutter 使用自己的 3.x 版本体系。版本数字彼此没有可比性。更有意义的是稳定发布窗口、平台新版本跟进速度、升级文档、原生扩展路径和自己依赖的插件是否及时适配。

框架之外还应治理 UI 库与原生依赖的升级风险:把 Expo SDK、React Native、动画库和任何 beta UI 库的升级拆成独立变更窗口,避免一次同时改动多层依赖后无法定位回归。

Expo SDK 57 的发布说明记录了一个与 Hermes V1 和 Reanimated 相关的已知内存回归:仅导入 Reanimated 也可能让应用内存增加约 25%–30%,官方给出的当前缓解方式是启用 worklets bundle mode,并表示后续 Hermes V1 修复正在推进。这不是拒绝 SDK 57 的理由,但它应进入首个技术尖峰:在目标 iPhone 和中低端 Android 真机上对比启动内存、列表滚动、弹层和长时间驻留,而不是只跑模拟器。Expo SDK 57 known regressions

市场响应:两边都已过“验证能否生产”的阶段#

能公开核验的信号如下:

信号Expo / React NativeFlutter应该怎样解读
项目方公布的规模Expo 官网称有 300 万以上开发者、700 万以上周下载、10 万以上活跃开发者、50 万以上项目和 10 万以上日构建;同时称 80% 的 React Native 开发者选择 ExpoFlutter 在 3.44 发布文中称有 150 万以上月活开发者,同比增加 50%,并称其为两大应用商店中第二流行的移动开发 SDK;pub.dev 过去 30 天有 13 亿以上 package 下载都是项目方自己的产品 / 生态统计,时间窗口与“开发者”“下载”定义不同,不能用来直接算 Expo 与 Flutter 的份额差
开源可见度快照expo/expo:51,489 stars、13,389 forksflutter/flutter:178,278 stars、30,948 forks2026-08-10 读取 GitHub API;star 更接近长期关注度,不等于活跃项目、营收或招聘量
生态参与Expo 官网列出 7 万以上 Discord 成员;Expo 工程师也持续参与 React Native release trainFlutter 3.44 公布 1,700+ 核心仓库贡献者、过去一年 5,800 个变更;官方 showcase 持续增加车载、金融、媒体等案例能说明社区和生产案例仍在增长;官方 showcase 是筛选后的案例,不能代表平均项目结果
人才与既有资产React / TypeScript 人才可以迁移大部分语言、状态与工具链知识,但高级移动开发仍需原生调试能力Dart / Flutter 是更专门的技能组合,成熟 Flutter 工程师可获得高度一致的端到端工具体验这是基于技术栈相邻性的工程推断,不是全球招聘统计;地区、薪资和岗位级别会显著改变结果

来源:Expo 官网公开指标 expo/expo Flutter 3.44 的生态数据 flutter/flutter Flutter production showcase

Flutter 还在 2026-08-06 公布了当年第二季度用户调查:3,500 多份完整答卷中,93% 表示总体正向满意,83% 相信 Flutter 能持续满足开发需求。这个结果很积极,但细项并非没有短板:Cupertino、Web 和桌面平台满意度低于 Dart、Android 与 core;不满反馈也集中在平台 / 生态成熟度与升级、工具、Bug 和稳定性。调查通过 IDE、官网和社交渠道面向 Flutter 用户招募,属于自选样本,适合观察现有社区情绪与痛点,不代表全球开发者市场份额。Expo 没有一份与其同时间、同问卷、同抽样方式的公开调查,因此不应人为制造一个“满意度对决”。Flutter Q2 2026 survey

React Native 生态内部也有一组比官网宣传更有参考价值的数据。Software Mansion 组织的 State of React Native 2025 在 2025-12-09 至 2026-01-08 收到 3,501 份答卷;按单题约 800–940 份有效回答计算,86.2% 的受访者过去一年使用过 Expo CLI,82.2% 使用 create-expo-app,68.2% 使用 EAS Build。它支持“Expo 已是活跃 React Native 社区中的主流工具链”这一判断,但仍是由 React Native 生态公司组织的公开自选、多选调查,不能外推为全球移动开发份额,也不能与 Flutter 调查的满意度百分比直接排名。调查方法 架构与升级 开发工具 构建与发布

这些数字支持两个谨慎结论:

  1. Flutter 的独立品牌和开源仓库可见度更大,移动、桌面、Web、车载与嵌入式的公开叙事也更广;它不是小众试验品。
  2. Expo 已成为 React Native 主流入口和交付平台,并获得 React Native 官方的框架推荐;它也早已不是只能做简单原型的工具。

不能据此得出的结论包括:“Flutter 的岗位一定更多”“Expo 的 App 数量一定更大”“某一方一定更容易融资”或“GitHub star 更多就更适合本项目”。如果人才供给是关键决策,应在目标招聘城市、相同职级和相同时间窗内抽样招聘网站,并把 React Native、Expo、Flutter、iOS、Android 的岗位去重后比较;全国或全球的单一搜索结果很容易被重复职位、外包职位和关键词污染。

长期维护信号同样健康,但仍要区分事实和含义:React Native、React、Metro 与 Yoga 已迁入 react GitHub 组织,并由独立的 React Foundation 长期管理;Flutter 的 2026 roadmap 表示非 Google 贡献者已多于 Google 员工,但路线图仍主要反映 Google 团队的工作;Expo 在 2026-04 宣布获得 4,500 万美元 Series B 融资。这些信息能说明治理在扩展、组织仍愿意投入,却不能替代产品级尽调,也不能直接换算成未来兼容性。React Native 0.86 Flutter / Dart 2026 roadmap Expo Series B

就当前项目而言,市场信号没有改变选型:两边都足够成熟;已有 React / TypeScript 资产和移动端交付经验会降低 Expo / React Native 的切换成本,而已有 Dart / Flutter 能力会降低 Flutter 的切换成本。GitHub 关注度不能抵消重写 UI 和业务运行时代码的成本。

Expo 与 Flutter 横向对比#

两者都能发布真正的 iOS / Android 原生应用,也都允许写 Swift、Kotlin 等平台代码。关键差异不是“真原生 / 假原生”,而是语言、渲染模型、生态和团队资产。

维度Expo / React NativeFlutter
主要语言TypeScript / JavaScript + ReactDart + Flutter Widget
UI 渲染React 树通过 Fabric 驱动原生 View / Text 等平台视图,也可接原生组件Flutter 自己实现大部分 Widget,并由引擎通过 Impeller 等路径绘制
平台一致性更容易随平台采用原生语义和交互,可用平台文件明确分支更容易获得跨平台视觉一致性,自绘和复杂动效控制集中
原生扩展Expo Modules、config plugins、TurboModules / Native Componentsplugins、platform channels、FFI、Platform Views
开发体验Metro、Fast Refresh、Expo Go / development build、Expo RouterFlutter tool、stateful hot reload、DevTools、统一 SDK
云构建发布EAS 提供一体化 Build、Submit、Update、Workflows;也可替换官方本地工具完整,CI/CD 通常由团队或第三方服务组装;本地 iOS 发布需 macOS / Xcode
OTAEAS Update 对相容的 JS / asset 更新提供一等能力Flutter SDK 不直接支持第一方 code push;第三方方案存在但不由 Flutter 官方背书,配置和内容更新仍可由后端完成
既有团队与代码资产React / TypeScript 团队可直接沿用语言、React 心智模型、无平台依赖的 schema、API client 和领域逻辑Dart / Flutter 团队可沿用现有 Widget、state、plugin 与发布实践;TypeScript 运行时代码须重写,可共享 OpenAPI / protobuf 等语言无关契约
自绘与图形常规产品 UI 表现成熟;重动画可用 Reanimated / Skia,仍需专项评测引擎统一控制渲染,对高度自绘、品牌动画和图形界面更自然
性能判断New Architecture + Hermes 足以覆盖大量产品型 App;必须用 release / profile 实测AOT + 自有渲染引擎具有可预测性;同样会受布局、图片、平台视图和业务代码影响
包生态风险npm 与原生依赖版本配套、config plugin 质量需要治理pub package、plugin 的双端实现与维护状态需要治理
人员能力React / TypeScript 团队迁移成本低已有 Dart / Flutter 团队优势明显

Flutter 官方架构说明指出,开发期通过 Dart VM 支持 stateful hot reload,iOS / Android 等原生平台发布时 AOT 编译为机器码,Web 则编译为 JavaScript / Wasm;Flutter 自己实现大部分 UI 控件并使用 Impeller 等渲染路径,同时可以通过 platform channels 和 Platform Views 接入原生代码与视图。Flutter architecture Platform-specific code

Web 与 OTA 边界来源:Expo Router static rendering Expo Router server rendering Flutter Web FAQ Flutter code push FAQ

不要用一句“Flutter 一定更快”或“React Native 一定更原生”做选择。两边都应在 release / profile 构建、目标低端设备和真实页面上测量。Flutter 官方的性能工具也强调应在 profile 模式检查帧时间,而不是用 debug 体验推断生产性能。Flutter Performance view Flutter performance best practices

已完成双端 Demo 后:独立开发者和中小项目的决策#

如果 Expo 与 Flutter 的 Demo 都已在目标真机稳定运行,而要做的是工具、内容、订阅、表单、交易、社区或轻量 SaaS 等常规产品型 App,我会把 Expo + React Native + TypeScript 作为独立开发者和独立中小项目的默认选择。这里的“更友好”不是指某个页面少写几行代码,而是从开发、排错、内部测试、签名、商店上传到线上修复的整条交付链,哪一方让一个人更少在基础设施之间切换。

Expo 的关键优势不应归因于 Expo Go。官方将 Expo Go 定位为学习和快速实验环境;实际项目应尽早使用自己的 development build。它可以包含项目所需的原生库和原生配置,日常 TypeScript / JavaScript 修改仍可直接连接开发服务器;只有增加或升级含原生代码的库、修改 app config,或升级 Expo SDK 时,才需要重新生成和编译 native runtime。再加上可选的 EAS Build、Submit 和 Update,一个人能用同一套工作流完成云构建、内测分发、商店上传与兼容 runtime 内的 JS / 资源更新。Expo development builds Expo workflow EAS Build

Flutter 也绝不是“不友好”。它的 stateful hot reload、Widget Inspector 和 DevTools 对持续打磨 UI、定位布局与性能问题非常顺手;不过 hot reload 不会重跑 main()initState(),改动 Kotlin、Java、Swift 或 Objective-C 仍要完整重启。它的发布能力同样成熟:flutter build appbundleflutter build ipa 可生成商店产物,官方连续交付指南还列出 Codemagic、Bitrise、Appcircle、fastlane 配合 GitHub Actions,以及 Xcode Cloud 等路径。差别不是 Flutter 不能自动化,而是相较 EAS 这类将云构建、签名协助、提交与更新整合在一起的可选服务,Flutter 项目通常自行选择并接通构建、签名、上传、内测分发与凭据管理的交付栈。Flutter Hot Reload Flutter DevTools Flutter continuous delivery

你的主要约束更值得优先选 Expo更值得优先选 Flutter
既有能力与可复用资产已熟悉 React / TypeScript,或已有 Web、API client、schema、设计 token 与前端工程习惯已熟悉 Dart / Flutter,或已有 Widget、插件、测试和发布资产
产品形态常规移动产品,最在意 MVP 到 TestFlight / Play 内测 / 线上修复的速度高度自绘、复杂品牌动效、跨端像素一致性是核心卖点
交付偏好希望以 development build + EAS 尽量收敛构建、分发和更新流程愿意自己组合并维护原生工程、CI/CD、签名与发布工具
原生依赖所需 React Native 库和 config plugin 已在两端真机验收关键 SDK 的 Flutter plugin 明显更成熟,或团队愿意维护 platform channel / 原生实现

因此,在没有明显 Flutter 优势的前提下,我会选 Expo。但如果你的 Demo 已经给出反证——例如 Flutter 在核心复杂界面、动画、性能或关键原生 SDK 上有可复现且对产品重要的明显优势——就应选 Flutter,不要为了“生态默认答案”放弃已验证的结果。反过来,重度 3D、游戏或深度平台定制也不自动等于 Flutter 胜出;它们都需要单独验证原生模块或专用引擎边界。

做最终决定前,建议再用两份 Demo 各完成一个相同的 vertical slice:登录、真实 API、复杂表单、推送或支付等一个原生能力、崩溃监控、预览包分发与一次修复发布。记录实现耗时、冷启动 / 内存、构建失败次数、排错时间和发布步骤数;实际摩擦更小的那一方才是你项目中的“开发者更友好”。

按解决方案和产品场景选择#

选型前先分清三种不同层次:Expo 与 Flutter 是应用框架,Unity / Godot / Unreal 是实时 2D / 3D 引擎,专业 CAD / BIM 栈还需要几何内核、约束求解器、行业格式 / 语义模型 SDK 等能力。 一个框架可以通过原生 View、纹理或模块接入后两者,但不能因为它“会画 UI”就推导出它已经具备游戏引擎或 CAD 能力。

产品场景默认建议Expo / React Native 的位置Flutter 的位置决策重点
电商、内容、社区、工具、订阅、SaaS 移动端React / TypeScript 团队优先 ExpoReact 业务、常规 UI 和 EAS 交付链可复用能胜任,但 UI 与业务运行时代码需以 Dart 重写团队与既有资产通常比渲染模型更重要
表单、审批、消息、离线任务、企业内部应用两者都适合;当前项目优先 Expo更容易与现有 React Web、API client、schema 和管理端协作已有 Flutter 团队时同样合理离线冲突、后台任务、设备管理和原生 SDK 才是主要风险
需要贴近 iOS / Android 语义的消费级 AppExpo / RN 略自然Host View、平台文件和原生组件边界清楚可以做平台分支,但常规 Widget 仍由 Flutter 自绘不要把“视觉统一”误当成“平台体验一致”
强品牌、跨平台像素统一、重 2D 动效、看板或触控大屏Flutter 值得 PoCReanimated / Skia 可以实现,但需要治理两套宿主差异统一引擎、自绘 Widget 与 DevTools 更集中真机 profile 测帧时间、内存、文字和无障碍
已有 React / TypeScript 团队、移动 App 为主Expo / React 体系更合适开发者、业务逻辑、API 契约和交付工具链能连续使用Flutter 需建立 Dart、Widget 与 plugin 的新能力团队切换成本与关键原生 SDK 相容性
新建桌面 / 嵌入式 / 车载多端产品Flutter 更值得进入候选Expo 的优势集中在 React Native 移动与 Web 工作流Flutter 的平台叙事和统一 SDK 覆盖更广逐个平台核对插件、输入方式、窗口和硬件支持
卡牌、答题、回合制、App 内小游戏两者都可普通 RN UI + Reanimated,或 Skia 画布;特别适合“游戏 + 账号 / 内容 / 商城”的 AppFlutter 官方 Casual Games Toolkit 提供模板,并建议实时游戏评估 Flame游戏循环、资源、音频、物理和生命周期都要验证
精灵、瓦片、实时轻量 2D 游戏做双边小型 PoCSkia Atlas、Reanimated worklet 或自建渲染循环Flame 生态通常更接近游戏框架心智若关卡编辑、物理和大量实体成为核心,比较专用引擎而非只比较 UI 框架
广告变现的休闲 / 超休闲 2D 小游戏若广告与实时游戏循环都是核心,优先 Flutter + Flame + google_mobile_ads;答题、卡牌等回合制玩法则按既有资产选择可以实现,但广告 SDK 是原生依赖;从 Day 1 使用 development build,核验 config plugin、双端原生配置与目标 Mediation adapterGoogle 提供官方 google_mobile_ads Plugin;Casual Games Toolkit 提供广告、IAP 等接入路线以同一广告 SDK 的 release PoC 验收关卡节奏、激励奖励、无填充、同意与前后台恢复
单个 3D 商品查看、旋转、缩放先用系统查看器;再评估内嵌渲染Expo 负责商品和交易壳,iOS 调 Quick Look,Android 调 Scene Viewer同样需要平台查看器、插件或独立 3D 层,没有天然 3D 优势USDZ 与 glTF / GLB 资产管线、回退图、真实比例和低端设备
重度 3D、多人实时、复杂物理或高质量特效Unity / Godot / Unreal 优先可作为账号、商城和设置外壳,3D 使用明确的全屏原生边界也只能作为外壳;Flutter UI 引擎不能替代游戏引擎场景 / 资源编辑器、物理、性能分析、引擎嵌入限制与团队能力
CAD / BIM、参数化建模、精确布尔和行业格式编辑专用 CAD / BIM SDK、几何内核组合或云 CAD / BIM 服务适合项目、协作、审批、标注和轻量查看壳同样只是客户端 UI 壳几何精度、约束、语义模型、格式许可、服务端算力、离线和数据安全

Flutter 在休闲游戏上的优势来自完整 Flutter 工具体验以及 Flame 等生态,而不是“Dart AOT 自动等于游戏引擎”。同理,Expo 的 Skia、GLView,以及 Expo 项目可集成的第三方 react-native-webgpu 路径提供了绘制入口,却不会自动补齐场景图、物理、资源编辑器和关卡流水线;WebGPU 是需要 development build 与独立兼容性验证的原生依赖,不是 Expo SDK 第一方模块。Flutter Casual Games Toolkit Expo GLView React Native Skia Atlas Expo SDK 56 对 WebGPU 的说明

这里的“广告变现小游戏”特指发布到 iOS / Android 的独立 App,不包括微信、抖音等平台各自的小游戏运行时。广告不会让某个框架自动获得更高的 fill rate 或 eCPM;它主要改变 SDK 接入、原生构建、调试和合规成本。Google 为 Flutter 提供官方 google_mobile_ads Plugin,Flutter 的 Casual Games Toolkit 也把广告、In-App Purchases、音频和 Crashlytics 等放入模板及配方。Expo / React Native 也能通过第三方 react-native-google-mobile-ads 接入,但它包含原生 SDK,不能在 Expo Go 中运行,应使用 config plugin 和 development / release build 验收;若目标 Mediation adapter 没有封装,两边都可能需要维护原生 iOS / Android 集成。这是 Flutter 在“广告是核心收入”的 2D 游戏中相对顺手、但并非绝对胜出的原因。Google Mobile Ads Flutter Plugin Flutter Ads overview Flutter Casual Games Toolkit React Native Google Mobile Ads:Expo 配置 Expo development builds

如果广告游戏还要卖“去广告”、永久解锁或订阅,Expo / React Native 与 Flutter 都可接 RevenueCat:前者使用 react-native-purchases,但 Expo Go 的 Preview API Mode 只用于模拟预览,真实购买必须在 development / release build 中验证;后者使用 purchases_flutter。两端仍要先在 App Store Connect 和 Google Play Console 创建商品,再导入并映射到 RevenueCat;它不替代商店审核,也不是广告 SDK。对“去广告”、高级功能或订阅可用 entitlement 建模;金币、体力、复活券等 consumable 不应挂在永久 entitlement,库存、发放、幂等和反作弊应由业务服务端或独立库存系统负责。RevenueCat Expo 安装 RevenueCat Flutter 安装 RevenueCat 商品配置 RevenueCat non-subscription purchases

广告的技术尖峰要和玩法一起验收:插屏放在关卡结算等自然断点并提前加载;展示全屏广告时暂停 game loop 与音频,关闭后恢复;激励广告必须由用户主动选择,并且只在 SDK 的奖励回调中结算一次奖励。开发和真机测试只用测试广告位或测试设备,不能反复点击正式广告。发布前还要实现 UMP 同意与隐私选项入口、完成商店广告披露,并让开发者网站上的 app-ads.txt 可被抓取和验证;Google Play 不允许在游戏操作中、关卡刚开始或其他意外时机展示干扰性全屏广告。AdMob interstitial ads AdMob rewarded ads UMP for Flutter app-ads.txt verification Google Play Ads policy

3D 家居设计:Expo 还是 Flutter#

先给直接答案:3D 家居产品默认应采用“Expo 产品壳 + 按复杂度选择的 3D 能力”,而不是因为需要 3D 就把整个 App 改写为 Flutter。 如果只是单件家具查看或摆放,Expo 足够;如果 3D 编辑是产品核心,应引入 Unity / 原生 AR;如果要求 CAD / BIM 级精确建模,则两者都不是几何内核。

Flutter 的 Impeller 负责 Flutter 场景的栅格化与渲染性能,不是带物理、场景编辑器、资产导入和 AR authoring 的高层 3D 引擎。Flutter 的低层 flutter_gpu API 截至整理日仍被官方标为 experimental,也要求项目从更底层建立自己的 renderer。因此,“Flutter 自己画 UI”不能推出“Flutter 天然更适合专业 3D 家居设计”Flutter Impeller flutter_gpu API flutter_gpu experimental 状态

家居需求层级推荐方案Expo / React Native 承担什么为什么不直接改 Flutter主要边界
商品目录、图片、视频、尺寸和购买Expo + React Native承担整个移动 App 的目录、交易、账户与项目流程没有能抵消 UI 与业务重写成本的收益常规图片、缓存、列表和交易风险
单模型 3D 查看Expo 壳 + iOS Quick Look / Android Scene Viewer商品页与 CTA 通过平台 adapter 打开全屏模型查看器Flutter 也需调用同一平台能力或第三方 rendereriOS 准备 USDZ;Android 准备 glTF / GLB;不支持时回退图片或 Web 查看器
“摆到我家”的单件家具 AR同上,使用 Quick Look 与 Scene Viewer 的 AR 模式管理 SKU、资产选择、收藏和购物车;AR 保持独立能力边界系统查看器决定覆盖面,换 UI 框架不会消除设备差异真实 1:1 比例、光照、ARCore / Google 服务、设备支持和 fallback
换颜色 / 材质、热点、少量模块开关先做隔离的 Expo 3D screen 技术尖峰React / TypeScript 与后端维护配置规则和价格;3D 层只做即时预览Flutter 也要选择并验证高层 3D 库;Impeller 本身不提供配置器expo-gl 是低层 API,远程 JS 调试不可用、部分 WebGL2 方法未实现,旧 Android GPU 差异必须真机验证;不达标就切 Unity
大量组合、吸附 / 碰撞、复杂 shader、动画和长时间编辑Unity-first,或 Expo 壳 + 全屏 Unity 模块Expo 保留登录、项目、商品、订单、支付和设置;Unity 独占实时 3D 页面Flutter 在这里也只是另一个壳,不能解决引擎级问题Unity as a Library 的生命周期、内存、全屏边界、输入和原生构建复杂度
多家具、墙地面、遮挡、测量、保存场景的专业空间设计自研空间编辑器;Unity + AR Foundation 作为实时 3D / AR 基础,iOS 可额外接 RoomPlanExpo 可做 companion shell 与项目管理;3D / AR 通过 native integration 接入Flutter 没有官方高层 3D / AR authoring 层,仍需相同引擎或原生桥接AR Foundation 不自带墙体编辑、吸附规则、精密测量或持久化;Android 与 iOS 必须分别实现和验收
iOS-first 房间扫描与概念规划Swift RoomPlan + RealityKit / ARKit,Expo 本地模块包装React Native 负责账号、项目列表、导出和协作;扫描 View 使用 SwiftFlutter 也要 platform channel / native view,未减少关键原生工作需要具备 LiDAR Scanner 且满足 RoomPlan 系统要求的 iPhone / iPad;扫描结果适合概念规划起点,不等于 CAD 级精度保证;Android 另建 ARCore 路径
CAD / BIM 级编辑专用 CAD / BIM SDK、几何内核组合或云端服务;Unity 只做实时可视化Expo / Web 做项目、权限、审阅、标注和任务流程Flutter 同样不能代替 B-rep、约束求解器、BIM 语义或 DWG / Revit 编辑 SDK精度、单位、约束、行业格式、许可、模型简化、服务端成本和 IP 安全

Apple 的 AR Quick Look 可以在系统体验中预览 USDZ 模型,Google Scene Viewer 则接受 glTF / GLB 并提供不支持 AR 时的回退路径;这是单件家具展示与试摆成本最低的起点。Apple AR Quick Look Google Scene Viewer 当需求升级到自定义锚点、遮挡、碰撞、测量、多物体与场景保存时,再进入自研空间编辑器:Unity AR Foundation 或直接使用 ARKit / ARCore 只是实时 3D / AR 基础,不是开箱即用的家居设计器。房间结构扫描还应单独看 iOS RoomPlan;Android 的深度能力和设备覆盖需要独立设计,不能把 iOS 路径直接照搬。Unity AR overview Apple RoomPlan ARCore Depth

推荐架构如下:

flowchart LR
  M["Mobile:Expo / React Native"] --> API["业务 API / 配置合法性 / 价格"]
  M --> A["项目自有 3D Adapter"]

  A --> S["轻量:Quick Look / Scene Viewer"]
  A --> U["重度跨端:Unity / AR Foundation"]
  A --> I["iOS 特有:RoomPlan / RealityKit"]

  S --> CDN["3D Asset Manifest + CDN"]
  U --> CDN
  I --> API

3D 页面与应用页面应通过清晰接口协作,而不是混成一套渲染树。可以共享的是 OpenAPI / JSON Schema、SKU 与配置规则、应用自有的语义 token 值、事件协议,以及类似 skuversiondimensionschecksumusdzglbunityBundlefallbackImage 的资产 manifest。价格和合法组合应由后端作权威校验,3D 引擎只做低延迟预览,避免通过篡改客户端配置获得错误价格。

若选择 Unity as a Library,不要先承诺“把 Unity 像普通 React Native 卡片一样嵌在任意页面”。在标准许可与标准集成下,Unity 官方列出的限制包括一次只能运行一个 runtime,移动端集成面向全屏渲染;Unload 后仍会保留一部分内存,而 iOS 调用 Application.Quit 完全退出 Unity runtime 后,在同一 App session 不能重新加载。Unity Industry 等条件需要按实际许可另行核对。具体数值和行为应以采用版本为准,并用真实集成验证生命周期。Unity as a Library

第一个 3D 技术尖峰至少要覆盖:代表性 iPhone 与中低端 Android、冷启动与首个模型加载、交互帧时间、峰值内存、连续 20 分钟热稳定、前后台和二次进入、弱网 / 离线、1:1 尺度、AR 不支持时的回退,以及资产版本与缓存失效。凡引入 RoomPlan、Unity bridge、react-native-webgpu,或其他 Expo Go 未包含的原生 module / 配置,都必须使用 development build;Quick Look / Scene Viewer 若仅通过系统支持的链接路径启动,则不必然新增原生依赖,但生产项目仍应尽早用自己的 development build 验收完整链路。

什么时候应该改选 Flutter#

以下条件同时命中越多,Flutter 越值得做 PoC:

  • 团队已有成熟的 Dart / Flutter 工程、插件与发布流水线。
  • 核心卖点是高度自绘的 2D 界面、复杂品牌动画、数据可视化或跨平台严格一致的画面;若核心是重度 3D,应先比较专用引擎。
  • 不需要复用现有 React / TypeScript 运行时代码,或接受为 Dart 重写这些能力。
  • 团队愿意单独维护 Flutter design system,并接受 Web 管理端使用另一套前端栈。
  • 关键原生 SDK 在 Flutter 侧的 plugin 更成熟,且已在目标设备验证。

为什么当前方案仍推荐 Expo#

当前资产已经覆盖 React Web 与 React Native Pro,Expo 可以直接消费 Native 产品,并让 Web / Native 共享 token 和 TypeScript 领域层。对于登录、表单、列表、图表、消息、推送、地图、支付、订阅等典型产品型 App,这种资产复用和 EAS 交付链路通常比理论上的渲染差异更影响总体成本。

推荐在最终确认前做一个小型双端 vertical slice:登录、一个列表、一个详情页、一个复杂表单、一次深链和一个原生能力。只有当这条切片在目标低端设备上暴露出无法接受且难以修复的问题,再比较 Flutter PoC;不要先重写完整产品再寻找证据。

落地顺序#

  1. 建立骨架:用 create-expo-app 建项目,锁定 Expo SDK 与 peer dependency,建立 app/ / src/ 边界。
  2. 打通垂直切片:认证、列表、详情、表单、错误态、离线恢复和一个原生能力同时跑通 iOS / Android。
  3. 建立环境:development / internal preview 可使用 .dev / .preview 标识并行安装;store staging 与 production 共用最终商店标识和原生基线,使用不同 EAS profile / update channel。
  4. 建立质量门禁:类型、lint、测试、真机、可访问性、性能与隐私检查进入 CI。
  5. 建立商店链路:尽早注册 Apple / Google 账号,先走 TestFlight 和 Play 测试轨道;新个人 Play 账号要使用 Closed testing,不要等开发完成才处理设备与账号验证。
  6. 再接 OTA:先理解 runtime version 与回滚,再启用生产 EAS Update;原生变化始终重新构建。
  7. 控制 beta 风险:Pro 组件走适配层,精确锁版本,升级时执行双端视觉与交互回归。

发布前检查清单#

  • npx expo-doctor 通过,Expo SDK 与 React Native 依赖相容。
  • iOS bundle identifier 与 Android package name 已确定且没有使用临时值。
  • Apple / Google 账号类型、组织归属和人员权限设置正确。
  • 新个人 Google Play 账号已完成真实设备验证、Closed testing 与 production access 申请(如适用)。
  • 第三方依赖许可、lockfile 与 CI 安装凭据已核对;密钥、签名文件、service-account JSON 和其他 secret 均未提交 Git。
  • EXPO_PUBLIC_* 只包含可公开客户端配置。
  • iOS / Android 真机完成键盘、安全区、返回手势、深链、时区、字体缩放与深色模式测试。
  • development、internal preview、store staging、production 的 build / environment / channel 关系已记录;staging 与 production 的商店标识、appVersion 和原生基线一致。
  • EAS Update 有预发布、rollout、监控和回滚流程。
  • 如果使用 CloudKit,App ID、container、entitlement、provisioning、production schema 和 TestFlight 真机路径已经逐项验证。
  • 原生依赖的 privacy manifest、required reason API、权限用途文案、App Store 隐私和 Google Data safety 已按最终 archive / AAB 核对。
  • 截图、内容分级、隐私政策、IAP / 支付路径、可用审核账号、在线 backend 与 Review Notes 完整;没有靠 OTA 才出现的隐藏功能。
  • 如果包含游戏、3D 或 AR,已在代表性真机完成帧时间、峰值内存、发热、前后台、资源回退与连续使用测试。
  • TestFlight 与 Play 测试渠道验证通过后才进入生产。

参考资料#

Expo 与 React Native#

商店#

游戏、3D 与 AR#

Flutter#

本文共 28624 字,创建于 Aug 10, 2026

相关标签: Frontend, React, TypeScript, UI, TanStack, ByAI