Expo 全面指南:概念、工具栈、上架、HeroUI 跨平台架构与 Flutter 对比
8月 10, 2026
AI 参与说明(Agent:Codex):本文由 Codex 根据 Expo、React Native、Flutter、Apple、Google Play 与 HeroUI 的官方公开资料协助整理,并提供面向 iOS 与 Android 的通用架构建议。内容整理于 2026-08-10;产品能力、价格、SDK 版本、商店规则与账号要求可能变化,请以文末一手资料和当前控制台为准。示例使用占位符,不包含账号标识、许可证、签名凭据或项目密钥。
适用范围:本文面向准备从 Web / React 转向移动端的初学者,以及已经拥有 HeroUI Web 与 React Native Pro 权益、计划同时发布 iOS 和 Android 应用的团队。这里讨论的是通用产品型 App;游戏、重度 3D、音视频编辑、车机、医疗器械或大量自研原生 SDK 的项目,需要单独做技术验证。
先说结论#
在已经采用 React、TypeScript、HeroUI Web 和 HeroUI Native Pro 的前提下,默认方案应当是:
- 使用 Expo + React Native + TypeScript 开发一套 iOS / Android 业务代码。
- 使用 Expo Router 管理页面、深链与认证路由。
- 移动端只使用
heroui-native+heroui-native-pro;不要把 Web 的@heroui/react或@heroui-pro/react导入原生应用。 - Web 与移动端共享的是语义化 design token、业务模型、API 契约、校验规则和文案,不是渲染组件。
- 开发早期可以用 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 是成熟且值得考虑的替代方案,但它不能使用 HeroUI 的 React / React Native 组件,也不能直接复用 TypeScript 业务包。除非团队已有明显的 Dart / Flutter 优势,或产品的核心是高度自绘、跨平台像素一致的图形界面,否则本项目选择 Expo 的综合成本更低。
如果产品包含游戏或 3D 家居能力,也不要简单改写为“Flutter 更适合图形”。Expo 明确可以做 2D 与轻量游戏;单件家具查看 / AR 试摆可以由 Expo 壳调用系统 3D 查看器。复杂空间编辑应给 Unity、ARKit / ARCore 或 RoomPlan 建立独立边界,CAD / BIM 级编辑则需要专用几何内核。此时 Flutter 和 Expo 都只是产品壳,不是 3D / CAD 引擎。
截至 2026 年,两套技术都处于持续高强度迭代和大规模生产使用阶段,不存在一方已经“停止发展”的问题。真正影响本项目风险的,是团队资产是否匹配,以及 HeroUI Native Pro 仍处于 beta,而不是 Expo 或 Flutter 是否还有市场。
Expo 到底是什么#
Expo 不是另一套移动操作系统,也不是把网页塞进 WebView。它是建立在 React Native 之上的框架、工具链和可选云服务。Expo 官方目前将其描述为“带云服务的全栈 React Native 框架”;React Native 官方也建议新应用使用框架,并把 Expo 列为当前推荐的社区框架。Expo 首页 React Native:使用框架创建应用
可以把整个体系拆成三层:
- React Native 运行层:React、Hermes、Fabric、TurboModules、原生 iOS / Android View,以及必要的 Swift、Objective-C、Kotlin、Java、C++ 代码。
- Expo 框架层:Expo SDK、Expo CLI、Expo Router、Expo Modules、app config、Prebuild、config plugins、development build。
- EAS 云服务层:Build、Submit、Update、Workflows、Metadata、Hosting、Observe 等服务。
flowchart TB A["TypeScript / React 业务代码"] --> B["Expo Router + 应用状态与数据层"] B --> C["应用自有 UI 适配层"] C --> D["HeroUI Native OSS / Pro + Uniwind"] 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.76 起默认启用。它并不意味着 JavaScript “变成了 Swift 或 Kotlin”,而是让 React、C++ 核心与原生模块之间的交互更直接,也为并发 React 能力和同步原生接口提供基础。React Native New Architecture
从 .tsx 到 .ipa / .aab:Expo 的技术原理#
直接回答:Expo / React Native 不会把每个 TypeScript / React 组件翻译成等价的 Swift 或 Kotlin 源码。 最终产物的确是 Apple、Google 能签名和安装的原生 App,但业务代码、原生壳与 UI 渲染分别经过不同管线。
构建期发生了什么#
- TypeScript 类型在构建时被移除,JSX 被转换为 JavaScript。
tsc主要负责类型检查;Metro 负责解析模块、转换并生成 JavaScript bundle。 - 在当前默认的 Hermes 生产配置下,JavaScript bundle 进一步编译为 Hermes bytecode。它由 App 内置的 Hermes JavaScript 引擎执行,不是 ARM 机器码,也不是 Swift / Kotlin。
expo prebuild根据 Expo SDK 模板、app config、config plugins 与 autolinking 生成或配置ios/、android/原生工程。它是在生成“原生工程壳和配置”,不是把业务组件改写成两套原生业务代码。- Xcode / Gradle 分别编译原生入口、React Native / Expo runtime、Swift / Kotlin 模块和第三方原生依赖;Metro / Hermes 产物与图片、字体等资源一起被打进应用。
- 概念上,最终
.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 渲染管线可以概括为:
- Hermes 执行 React 业务逻辑,在 JavaScript 中产生 React Element Tree。
- Fabric 为 host component 创建 C++ React Shadow Tree。自定义业务组件会继续归约为
<View>、<Text>、<Image>等 host component,并不会每个都创建原生节点。 - Yoga 计算大部分布局;文本等部分尺寸仍需向宿主平台测量。
- Commit 后,Fabric 对前后两棵树做 diff,并在 UI thread 的 Mount 阶段创建或更新平台 Host View。Android 可对应
ViewGroup、TextView,iOS 对应UIView等宿主视图。 - 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 怎样变成像素#
- Widget 是不可变的 UI 配置描述;
build()根据状态产生新的 Widget 子树。 - Element Tree 保存 Widget 在界面中的长期位置、状态与生命周期,并决定哪些部分需要更新。
- RenderObject Tree 负责约束传递、尺寸与位置计算、绘制和命中测试。它是 Element Tree 的子集,不是一棵 UIKit / Android View 树。
- Paint 与 compositing 生成场景和图层;Flutter Engine 通过 Impeller 等受支持的渲染路径把场景栅格化并交给 GPU。
- 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 Native | Flutter |
|---|---|---|
| 业务代码发布形态 | 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 Modules | plugins、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 / HeroUI Native 资产,后者更容易实现跨平台像素一致的自绘界面。
初学者最容易混淆的概念#
| 名称 | 它是什么 | 什么时候使用 |
|---|---|---|
| React Native | 用 React 描述移动 UI、调用原生平台能力的框架 | 应用真正的跨平台运行基础 |
| Expo SDK | 与 Expo SDK 版本配套的一组原生模块和 JavaScript API | 相机、通知、文件、SQLite、SecureStore、图像等能力 |
| Expo CLI | 随项目使用的命令行工具,通常通过 npx expo 调用 | 启动 Metro、安装兼容依赖、本地编译、诊断和 Prebuild |
| Metro | React 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/ | 需要本地原生工程或构建原生二进制时 |
| CNG | Continuous Native Generation,把原生工程视为可再生成产物的工作流 | 降低双端原生工程升级和配置漂移成本 |
| Config plugin | 在 Prebuild 阶段以可重复脚本修改原生工程 | 权限、entitlement、Info.plist、Manifest、Gradle 等配置 |
| EAS | Expo 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 内积分、养成、教育互动或小游戏 | 很适合 | HeroUI 负责登录、商城、设置和普通页面;高频画面交给 Skia / GL 渲染层 |
| 精灵、瓦片地图、简单街机、轻量 2D 物理 | 可行,需要专项性能验证 | 优先 Skia Canvas / Atlas,物理、固定时步游戏循环和资产系统由项目补齐 |
| 简单 3D 模型展示、旋转、缩放、材质切换 | 可以做 PoC | 用 expo-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 preview | iOS ad hoc、Android APK,通过内部链接安装 | 否,不是 TestFlight / Play track 产物 |
| 商店验收 | Store staging / release build | TestFlight、Play Internal / Closed,使用最终商店标识与 AAB / IPA | 是 |
| 生产 | Store production build | App Store / Google Play 审核与分阶段发布 | 是 |
Expo 官方已明确把 Expo Go 定位为学习和快速实验环境。日常开发使用 development build;内部链接评审可以使用 internal distribution;TestFlight、Play 测试轨道与正式发布必须使用 store release build。Development builds 分发评审版本
如果只是先做一个不含自定义原生能力的原型,而且 Expo Go 已支持所选 SDK 与依赖,可以用 npx expo start 获得最快反馈;这不是正式项目必须通过的门槛。采用 HeroUI Native Pro 的项目应尽早直接建立 development build,因为 SDK 过渡、额外原生 peer dependency、Metro 配置和后续支付、推送、地图等能力都可能要求自有开发构建。
还要注意版本过渡。整理本文时,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 Router | app/ 只放路由与 layout,组件和业务代码放到 src/ |
| UI | heroui-native | 稳定基础组件,优先覆盖按钮、表单、Overlay 等通用能力 |
| Pro UI | heroui-native-pro | 只引入确有价值的日期、图表、Stepper 等组件,并用应用适配层隔离 beta API |
| 样式 | Tailwind CSS v4 + Uniwind | 通过语义 token 统一浅色 / 深色主题,不直接复制 Web CSS |
| 动画与手势 | Reanimated + Gesture Handler | 使用 HeroUI 要求的相容版本,不重复安装不相容副本 |
| 远程数据 | 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 / Workflows | development、internal preview、store staging、production 分层;只有 release build 固定 update channel |
后端不应由 UI 框架决定。REST、GraphQL、tRPC、BaaS 或自建服务都可以;移动端只依赖稳定的 API 契约、认证协议和错误模型。若已有 Web 后端,应先复用 API schema 与业务类型,再决定是否共享具体 TypeScript 包。
HeroUI Web 与 HeroUI Native 的真实边界#
HeroUI 的 Web 与 Native 产品共享设计语言,但不是同一套运行时:
| 平台 | OSS 包 | Pro 包 | 底层 |
|---|---|---|---|
| Web | @heroui/react + @heroui/styles | @heroui-pro/react | DOM、React Aria、浏览器 CSS、Tailwind CSS v4 |
| iOS / Android | heroui-native | heroui-native-pro | React Native primitives、Uniwind、Reanimated、Gesture Handler |
HeroUI Native 官方目前也不建议把 Native 组件用于 Expo Web,而是建议 Web 使用 HeroUI React。HeroUI Native Quick Start
截至整理日,Web OSS @heroui/react 为稳定版 3.2.4,Native OSS heroui-native 为稳定版 1.0.8;Web Pro @heroui-pro/react 与 Native Pro heroui-native-pro 的最新公开版本均为 1.0.0-beta.8。团队接受 beta 变更风险、精确锁版本并完成平台回归后可以采用 Pro,但这不等同于 GA 稳定性保证。HeroUI React releases HeroUI Native releases HeroUI React Pro releases HeroUI Native Pro releases
Web v3 使用 React 19+、Tailwind CSS v4,并按 tailwindcss → @heroui/styles → @heroui-pro/react/css 的顺序导入 CSS;它不需要 HeroUI Provider。只有 Native 端需要后文的 GestureHandlerRootView 与 HeroUINativeProvider。两套入口不能互换。HeroUI React Quick Start HeroUI React Pro installation
推荐采用三条隔离规则:
- 页面和 feature 不直接到处导入 Pro 组件,而是从
src/ui/中的应用适配器导入。 - 精确锁定 Pro 与 peer dependency 版本并提交 lockfile;升级单独开变更,跑完双端回归再合并。
- 优先使用 OSS 稳定组件;只有日期、图表、复杂选择器等确实节约明显工程成本的场景才使用 Pro。
HeroUI Pro 当前的 Web Hero、Mobile Hero 与 Super Hero 分别对应 Web、React Native 和两端权益。团队应通过 npx heroui-pro status 核对实际 entitlement,而不是只根据“Pro”字样推断。CI / EAS 安装 Native Pro 时使用仅存在于服务端的 HEROUI_AUTH_TOKEN;本机使用 HEROUI_PERSONAL_TOKEN,二者不能互换,也不能进入客户端包。Native Pro 安装 Native Pro licensing
按当前官方条款,Individual 许可只供一名具名开发者使用;Team 按能够访问 Pro 资产的具名员工或承包商计 seat,且至少两个 seat。同一许可可用于不限数量的商业项目,首购包含一年更新,已取得版本可永久使用;最终 App 可以分发,但不能发布、转授或销售 Pro 组件、模板、设计资产及其源码。公开仓库应提交依赖声明与 lockfile,却不能提交 node_modules、Pro 包内容、模板或设计资产副本。许可与方案可能调整,扩充团队前应重新核对当前条款。HeroUI Pro Terms
设计系统应该共享什么#
正确共享:
accent、background、surface、danger等语义颜色名。- 字体角色、字号层级、圆角、间距、阴影意图和动效原则。
- 图标命名、文案语气、表单校验与可访问性规则。
- 业务类型、API schema、日期与货币格式化规则。
不应直接共享:
- Web 的 DOM / React Aria 组件实现。
@heroui-pro/react/css、BEM 组件 CSS 或浏览器专用样式。- Native 的 Uniwind class 扫描结果、Portal、手势和原生 Overlay 实现。
- 为了“复用率”而强行统一 iOS、Android、Web 的导航和交互细节。
如果团队使用 HeroUI Pro Design Systems,可以把同一设计意图分别导出 Web CSS 与 Native CSS,再把两份生成物放到各自应用。它们拥有共同 token 契约,但仍是两个平台产物。这样既能保持品牌一致,也不会假装 DOM 和 React Native 能共享渲染代码。HeroUI Pro Design Systems
推荐目录结构#
如果当前只做 iOS 和 Android,先使用单一 Expo App;不要仅因为已经购买 Web 权益就引入 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/
│ ├── primitives/
│ └── pro-adapters/
├── api/
│ ├── client.ts
│ └── errors.ts
├── design/
│ ├── tokens.ts
│ └── theme.ts
├── storage/
├── config/
└── test/
assets/
global.css
app.config.ts
eas.jsonapp/ 只负责路由入口和 layout。业务组件、请求、类型、存储和设计系统不与路由文件混放。这样可以在不改 URL 结构的情况下重构 feature,也能让平台特化文件使用 .ios.tsx、.android.tsx 或 .native.tsx。
如果已经存在需要长期维护的 Web 管理端,再升级为:
apps/mobile # Expo + heroui-native(-pro)
apps/web # React Web + @heroui/react + @heroui-pro/react
packages/core # API schema、业务类型、无平台依赖的领域逻辑
packages/design-contract # token 名称、品牌语义、生成约定不要建立一个所谓 packages/ui,然后试图同时导出 DOM 和 React Native 组件。更诚实的方式是 ui-web / ui-native 分开,或使用 .web.tsx / .native.tsx 明确平台实现。
最小 HeroUI Native 根配置#
新项目可以从 HeroUI Native 官方 scaffold 开始,它会创建 Expo + TypeScript 项目并配置 Uniwind、全局 CSS 与 Provider:HeroUI Native Quick Start
npx create-heroui-native-app@latest
cd my-app
npx heroui-pro@latest login
npx heroui-pro@latest install
npx expo-doctor登录命令只用于开发者本机。不要把登录状态、license、personal token 或下载后的私有 Pro 源码发布到公共仓库。
app/_layout.tsx 至少保留手势根节点和 HeroUI Native Provider:
import "../global.css";
import { Stack } from "expo-router";
import { HeroUINativeProvider } from "heroui-native/provider";
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function RootLayout() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<HeroUINativeProvider>
<Stack>
<Stack.Screen name="(public)" options={{ headerShown: false }} />
<Stack.Screen name="(app)" options={{ headerShown: false }} />
</Stack>
</HeroUINativeProvider>
</GestureHandlerRootView>
);
}全局样式按平台文档的顺序导入。Native OSS 从 heroui-native 1.0.8 起会自行注册 source,但 Native Pro 当前仍要求把 Pro source 加入扫描:
@import "tailwindcss";
@import "uniwind";
@import "heroui-native/styles";
@import "heroui-native-pro/styles";
@source "./node_modules/heroui-native-pro/lib";随后再追加团队导出的 Native design token。不要把 Web 的 @heroui/styles 或 @heroui-pro/react/css 放进这个文件。
如果旧项目锁在 heroui-native 1.0.8 之前,还需要按该版本文档保留 OSS 的 @source;不要照抄新项目配置。业务组件若采用 heroui-native/button 等 granular export,应在整个应用保持一致,任何一次从 heroui-native 根入口导入组件都会削弱这项包体优化。
应用配置与四层交付环境#
下面的 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 则使用最终商店标识和相同原生依赖 / 配置基线,但分别接收 staging、production 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,或在评估后采用 fingerprint。App variants Runtime versions
EAS Submit 只负责把 .ipa / .aab 上传到 App Store Connect 或 Google Play 对应轨道,不等于已经提交审核,更不等于对公众发布。截图、metadata、协议、Data safety / 隐私资料、审核账号、选择 build、App Review 与分阶段 release 仍需在商店侧完成。Submit to app stores
构建安装阶段需要能够读取 HeroUI 的 CI/CD token;应在 EAS Dashboard 或 CI secret store 中以 secret 可见性创建 HEROUI_AUTH_TOKEN,不要把本机的 HEROUI_PERSONAL_TOKEN 用于云构建。EXPO_PUBLIC_* 会被内联到客户端 JavaScript 中,任何用户都可能读取,绝不能存放 HeroUI token、数据库密码、服务账号 JSON、Apple 私钥或后端 API secret。Expo 环境变量 HeroUI Native Pro CI/CD
数据、认证与安全边界#
一个可维护的移动端数据层至少应区分三类状态:
- 远程状态:API 返回的数据、分页、缓存、重试和失效。简单项目使用
fetch+ adapter 即可;缓存关系复杂时再引入 TanStack Query。 - 本地 UI 状态:当前 Tab、弹窗、筛选条件和未提交表单,优先保留在组件或 feature 内。
- 持久化状态:令牌、设置和离线数据。敏感小数据进入 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 的启动、登录、深链、离线恢复、推送、键盘和返回键通过真机检查。
- HeroUI Overlay、日期时区、深色模式、字体缩放、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 中使用 CloudKit、CKContainer、CKDatabase 和 CKRecord。但截至 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-picker 与 ios.usesIcloudStorage: true | getDocumentAsync 是选择 / 导入流程,默认把选中文件复制到 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,不用重写页面。HeroUI 只负责 UI,与 CloudKit 选择没有冲突。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
Apple 对 React Native、Expo 或 Flutter 有额外审核限制吗#
没有针对这三个框架的公开禁令,也没有“App 必须用 Swift / SwiftUI 编写”的规则。 这是根据 2026-08-10 现行规则得出的结论,不代表 Apple 对任何框架的认证,也不保证个别 App 通过。Apple 审核最终 .ipa 的公开 API 使用、完整性、体验、内容、隐私和商业行为,而不是组件源码用 React 还是 Dart 描述。Apple 的当前常用第三方 SDK 清单甚至明确列出 Flutter、hermes 和 UnityFramework;这意味着在 Apple 规定的提交场景下需要 SDK privacy manifest,作为二进制依赖使用时还需要 SDK signature,并不是禁用或背书。App Review Guidelines Apple 第三方 SDK 要求
| 审核点 | 对 Expo / React Native / Flutter 的实际含义 |
|---|---|
| 公开 API、完整性与性能 | 只使用公开 API,提交最终、真机测试过、后端可用的版本;崩溃、占位内容、明显卡顿、过度发热和无法访问的审核路径都会造成问题,与框架无关 |
| Minimum Functionality / 模板 | 4.2 要求 App 超越重新打包的网站,具备足够效用或娱乐价值。购买 HeroUI 模板本身不违规;只换 Logo / 内容就用多个 Bundle ID 批量上架的白标克隆,才是 4.2.6 / 4.3 的主要风险 |
| EAS Update / OTA | Apple 2.5.2 不允许下载、安装或执行会引入或改变 App 功能的代码,EAS Update 没有特别豁免,Flutter 的第三方 code push 也受同一原则约束。生产 OTA 应保守用于已审核、已宣传能力范围内的缺陷修复、文案 / 资源和小幅实现修正;新功能、主要用途、商业模式、支付或隐私行为的实质改变应提交新二进制。不得用 OTA 激活审核时隐藏的功能、外部支付或代码 / 插件商店。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 外消费的现实服务使用其他支付。区域、reader、enterprise、multiplatform 等例外会变,应按上架 storefront 重新核对;随机虚拟物品还需在购买前披露概率 |
| AR / 3D 家居 | 4.2.1 明确说使用 ARKit 时应提供丰富、紧密集成的 AR 体验;只把一个模型放进 AR View 或重放动画不足以构成完整价值。尺寸、摆放、保存方案、商品配置或房间扫描等应与主业务真正结合 |
| 审核访问 | 登录型 App 提供有效 demo account 或完整 demo mode,保持 backend 可访问,并在 Review Notes 里说清非显而易见的原生能力、订阅、游戏购买和 OTA 机制 |
App Review Guidelines 是持续更新的 living document,区域支付规则和第三方 SDK 清单尤其容易变化。每次提交前都应重新阅读英文当前版和开发者账号里已接受的协议,不要把某次成功上架当成永久先例。
EAS 是否必须付费#
Expo 框架、SDK 和 CLI 是开源的;应用也可以用本地或其他 CI 构建。EAS 提供有限量的免费低优先级 Build 和 Update 配额,超出后按当前套餐计费。不要把 EAS 费用与 Apple / Google 会员费混为一谈。EAS plans
2026 年的迭代状态与市场响应#
本节是 2026-08-10 的时间切片。版本号、下载量、开发者规模和 GitHub 数据都会变化;其中“开发者规模”“市场排名”等数字来自项目方公开口径,不应当作独立审计的市场份额。
迭代状态:都很活跃,但升级节奏不同#
| 维度 | Expo / React Native | Flutter |
|---|---|---|
| 当前生产基线 | 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.44.9、Dart 3.12.2;3.47 仍在 beta |
| 稳定发布节奏 | 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 仍在 beta,另有通常按月发布的 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,并开始拆分 Material / Cupertino;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 Flutter / Dart 2026 roadmap
这里最容易犯的错误,是只看“最新版本号”判断活跃度。Expo 的版本号较小,是因为它使用 SDK 序号并跟随 React Native;Flutter 使用自己的 3.x 版本体系。版本数字彼此没有可比性。更有意义的是稳定发布窗口、平台新版本跟进速度、升级文档、原生扩展路径和自己依赖的插件是否及时适配。
对本文方案而言,框架之外还有一层更具体的风险:heroui-native-pro 在整理日仍是 1.0.0-beta.8。因此应把 Pro 组件升级与 Expo SDK 升级拆成两个变更窗口,避免同时更换 React Native、HeroUI、Uniwind 和 Reanimated 后难以定位回归。
此外,HeroUI Native 会使用 Reanimated。Expo SDK 57 的发布说明记录了一个已知回归:在 Hermes V1 下,仅导入 Reanimated 就可能让应用内存增加约 25%–30%,官方给出的当前缓解方式是启用 worklets bundle mode,并表示后续 Hermes V1 修复正在推进。这不是拒绝 SDK 57 的理由,但它应进入首个技术尖峰:在目标 iPhone 和中低端 Android 真机上对比启动内存、列表滚动、Overlay 和长时间驻留,而不是只跑模拟器。Expo SDK 57 known regressions
市场响应:两边都已过“验证能否生产”的阶段#
能公开核验的信号如下:
| 信号 | Expo / React Native | Flutter | 应该怎样解读 |
|---|---|---|---|
| 项目方公布的规模 | Expo 官网称有 300 万以上开发者、700 万以上周下载、10 万以上活跃开发者、50 万以上项目和 10 万以上日构建;同时称 80% 的 React Native 开发者选择 Expo | Flutter 在 3.44 发布文中称有 150 万以上月活开发者,同比增加 50%,并称其为两大应用商店中第二流行的移动开发 SDK;pub.dev 过去 30 天有 13 亿以上 package 下载 | 都是项目方自己的产品 / 生态统计,时间窗口与“开发者”“下载”定义不同,不能用来直接算 Expo 与 Flutter 的份额差 |
| 开源可见度快照 | expo/expo:51,489 stars、13,389 forks | flutter/flutter:178,278 stars、30,948 forks | 2026-08-10 读取 GitHub API;star 更接近长期关注度,不等于活跃项目、营收或招聘量 |
| 生态参与 | Expo 官网列出 7 万以上 Discord 成员;Expo 工程师也持续参与 React Native release train | Flutter 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 调查的满意度百分比直接排名。调查方法 架构与升级 开发工具 构建与发布
这些数字支持两个谨慎结论:
- Flutter 的独立品牌和开源仓库可见度更大,移动、桌面、Web、车载与嵌入式的公开叙事也更广;它不是小众试验品。
- 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
就当前项目而言,市场信号没有改变选型:两边都足够成熟,而已有 HeroUI Web / Native Pro 与 React / TypeScript 资产,只能在 Expo / React Native 方案中直接兑现。Flutter 更大的仓库关注度,并不足以抵消 UI 和业务层重写成本。
Expo 与 Flutter 横向对比#
两者都能发布真正的 iOS / Android 原生应用,也都允许写 Swift、Kotlin 等平台代码。关键差异不是“真原生 / 假原生”,而是语言、渲染模型、生态和团队资产。
| 维度 | Expo / React Native | Flutter |
|---|---|---|
| 主要语言 | TypeScript / JavaScript + React | Dart + Flutter Widget |
| UI 渲染 | React 树通过 Fabric 驱动原生 View / Text 等平台视图,也可接原生组件 | Flutter 自己实现大部分 Widget,并由引擎通过 Impeller 等路径绘制 |
| 平台一致性 | 更容易随平台采用原生语义和交互,可用平台文件明确分支 | 更容易获得跨平台视觉一致性,自绘和复杂动效控制集中 |
| 原生扩展 | Expo Modules、config plugins、TurboModules / Native Components | plugins、platform channels、FFI、Platform Views |
| 开发体验 | Metro、Fast Refresh、Expo Go / development build、Expo Router | Flutter tool、stateful hot reload、DevTools、统一 SDK |
| 云构建发布 | EAS 提供一体化 Build、Submit、Update、Workflows;也可替换 | 官方本地工具完整,CI/CD 通常由团队或第三方服务组装;本地 iOS 发布需 macOS / Xcode |
| OTA | EAS Update 对相容的 JS / asset 更新提供一等能力 | Flutter SDK 不直接支持第一方 code push;第三方方案存在但不由 Flutter 官方背书,配置和内容更新仍可由后端完成 |
| Web 资产复用 | React / TypeScript 生态更接近现有 Web;React Native Web 输出 DOM,Expo Router 可做 SSG;SDK 55 起的 SSR 仍是 alpha,需要运行时 server 托管并单独评估,当前同一项目不能混用 static / server rendering | Flutter Web 可编译到 JS / Wasm,适合 PWA、SPA 或现有 Flutter App 的 Web 版;不能复用 React / HeroUI,官方也不建议用于富文本静态、强 SEO 页面 |
| HeroUI 复用 | 可直接使用 heroui-native / heroui-native-pro,并共享 HeroUI token | 没有 HeroUI Flutter runtime;需重建 Widget 与主题映射 |
| 业务代码复用 | 可与 React Web 共享无平台依赖的 TypeScript schema、API client 和领域逻辑 | Dart 与 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
按解决方案和产品场景选择#
选型前先分清三种不同层次:Expo 与 Flutter 是应用框架,Unity / Godot / Unreal 是实时 2D / 3D 引擎,专业 CAD / BIM 栈还需要几何内核、约束求解器、行业格式 / 语义模型 SDK 等能力。 一个框架可以通过原生 View、纹理或模块接入后两者,但不能因为它“会画 UI”就推导出它已经具备游戏引擎或 CAD 能力。
| 产品场景 | 默认建议 | Expo / React Native 的位置 | Flutter 的位置 | 决策重点 |
|---|---|---|---|---|
| 电商、内容、社区、工具、订阅、SaaS 移动端 | 当前项目优先 Expo | HeroUI Native 直接服务页面,React / TypeScript 资产与 EAS 交付链可复用 | 能胜任,但需要重写 HeroUI 与 TypeScript 运行时代码 | 团队与既有资产通常比渲染模型更重要 |
| 表单、审批、消息、离线任务、企业内部应用 | 两者都适合;当前项目优先 Expo | 更容易与现有 React Web、API client、schema 和管理端协作 | 已有 Flutter 团队时同样合理 | 离线冲突、后台任务、设备管理和原生 SDK 才是主要风险 |
| 需要贴近 iOS / Android 语义的消费级 App | Expo / RN 略自然 | Host View、平台文件和原生组件边界清楚 | 可以做平台分支,但常规 Widget 仍由 Flutter 自绘 | 不要把“视觉统一”误当成“平台体验一致” |
| 强品牌、跨平台像素统一、重 2D 动效、看板或触控大屏 | Flutter 值得 PoC | Reanimated / Skia 可以实现,但需要治理两套宿主差异 | 统一引擎、自绘 Widget 与 DevTools 更集中 | 真机 profile 测帧时间、内存、文字和无障碍 |
| Web SEO / 富文本官网 + 移动 App + React 管理端 | Expo / React 体系更合适 | Web 使用 DOM、HeroUI Web;移动使用 HeroUI Native;共享 TypeScript core | Flutter Web 更适合 App-like PWA / SPA,不适合把富文本 SEO 站点强行统一 | 共享领域层和 token,不共享渲染组件 |
| 新建桌面 / 嵌入式 / 车载多端产品 | Flutter 更值得进入候选 | Expo 的优势集中在 React Native 移动与 Web 工作流 | Flutter 的平台叙事和统一 SDK 覆盖更广 | 逐个平台核对插件、输入方式、窗口和硬件支持 |
| 卡牌、答题、回合制、App 内小游戏 | 两者都可 | 普通 RN UI + Reanimated,或 Skia 画布;特别适合“游戏 + 账号 / 内容 / 商城”的 App | Flutter 官方 Casual Games Toolkit 提供模板,并建议实时游戏评估 Flame | 游戏循环、资源、音频、物理和生命周期都要验证 |
| 精灵、瓦片、实时轻量 2D 游戏 | 做双边小型 PoC | Skia Atlas、Reanimated worklet 或自建渲染循环 | Flame 生态通常更接近游戏框架心智 | 若关卡编辑、物理和大量实体成为核心,比较专用引擎而非只比较 UI 框架 |
| 单个 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 的说明
3D 家居设计:Expo 还是 Flutter#
先给直接答案:对于已有 HeroUI Web + HeroUI Native Pro 的团队,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 / HeroUI 承担什么 | 为什么不直接改 Flutter | 主要边界 |
|---|---|---|---|---|
| 商品目录、图片、视频、尺寸和购买 | Expo + HeroUI Native | 承担整个移动 App;Web 继续使用 HeroUI Web | 没有能抵消 UI 与业务重写成本的收益 | 常规图片、缓存、列表和交易风险 |
| 单模型 3D 查看 | Expo 壳 + iOS Quick Look / Android Scene Viewer | 商品页与 CTA 使用 HeroUI;平台 adapter 打开全屏模型查看器 | Flutter 也需调用同一平台能力或第三方 renderer | iOS 准备 USDZ;Android 准备 glTF / GLB;不支持时回退图片或 Web 查看器 |
| “摆到我家”的单件家具 AR | 同上,使用 Quick Look 与 Scene Viewer 的 AR 模式 | 管理 SKU、资产选择、收藏和购物车;AR 保持独立能力边界 | 系统查看器决定覆盖面,换 UI 框架不会消除设备差异 | 真实 1:1 比例、光照、ARCore / Google 服务、设备支持和 fallback |
| 换颜色 / 材质、热点、少量模块开关 | 先做隔离的 Expo 3D screen 技术尖峰 | HeroUI 做配置面板;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 可额外接 RoomPlan | Expo 可做 companion shell 与项目管理;3D / AR 通过 native integration 接入 | Flutter 没有官方高层 3D / AR authoring 层,仍需相同引擎或原生桥接 | AR Foundation 不自带墙体编辑、吸附规则、精密测量或持久化;Android 与 iOS 必须分别实现和验收 |
| iOS-first 房间扫描与概念规划 | Swift RoomPlan + RealityKit / ARKit,Expo 本地模块包装 | HeroUI 继续做账号、项目列表、导出和协作;扫描 View 使用 Swift | Flutter 也要 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 W["Web:React + HeroUI Web"] --> API["业务 API / 配置合法性 / 价格"] M["Mobile:Expo + HeroUI Native"] --> 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
这里仍然坚持“不共享渲染组件”:HeroUI Web、HeroUI Native 与 Unity UI 不是同一套 runtime。可以共享的是 OpenAPI / JSON Schema、SKU 与配置规则、应用自有的语义 token 值、事件协议,以及类似 sku、version、dimensions、checksum、usdz、glb、unityBundle、fallbackImage 的资产 manifest。价格和合法组合应由后端作权威校验,3D 引擎只做低延迟预览,避免通过篡改客户端配置获得错误价格。
若选择 Unity as a Library,不要先承诺“把 Unity 像普通 HeroUI 卡片一样嵌在任意页面”。在标准许可与标准集成下,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,应先比较专用引擎。
- 不需要复用 HeroUI React / Native 包,也不依赖现有 TypeScript 业务代码。
- 团队愿意单独维护 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;不要先重写完整产品再寻找证据。
落地顺序#
- 建立骨架:用 HeroUI Native scaffold 建项目,锁定 Expo SDK、HeroUI 和 peer dependency,建立
app//src/边界。 - 打通垂直切片:认证、列表、详情、表单、错误态、离线恢复和一个原生能力同时跑通 iOS / Android。
- 建立环境:development / internal preview 可使用
.dev/.preview标识并行安装;store staging 与 production 共用最终商店标识和原生基线,使用不同 EAS profile / update channel。 - 建立质量门禁:类型、lint、测试、真机、可访问性、性能与隐私检查进入 CI。
- 建立商店链路:尽早注册 Apple / Google 账号,先走 TestFlight 和 Play 测试轨道;新个人 Play 账号要使用 Closed testing,不要等开发完成才处理设备与账号验证。
- 再接 OTA:先理解 runtime version 与回滚,再启用生产 EAS Update;原生变化始终重新构建。
- 控制 beta 风险:Pro 组件走适配层,精确锁版本,升级时执行双端视觉与交互回归。
发布前检查清单#
-
npx expo-doctor通过,Expo SDK 与 React Native 依赖相容。 - iOS bundle identifier 与 Android package name 已确定且没有使用临时值。
- Apple / Google 账号类型、组织归属和人员权限设置正确。
- 新个人 Google Play 账号已完成真实设备验证、Closed testing 与 production access 申请(如适用)。
- HeroUI entitlement 已用
npx heroui-pro status核对;个人 token 与 CI token 分离。 - Pro 依赖声明与 lockfile 已提交;
HEROUI_PERSONAL_TOKEN、HEROUI_AUTH_TOKEN、node_modules、Pro 包 / 模板 / 设计资产内容、签名文件、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#
- Expo
- 创建 Expo 项目
- Expo 核心概念
- Development builds
- Continuous Native Generation
- Expo Modules API
- Expo Modules 入门
- Expo app config
- Expo iOS capabilities
- Expo DocumentPicker
- Expo GLView
- Expo 中的 React Native Skia
- Expo Router
- EAS Build
- EAS Submit
- EAS Update
- React Native New Architecture
- React Native render pipeline
- React Native glossary
- Hermes
HeroUI#
- HeroUI Native Quick Start
- HeroUI Native theming
- HeroUI React Pro releases
- HeroUI Native Pro introduction
- HeroUI Native Pro installation
- HeroUI Native Pro components
- HeroUI Native Pro releases
- HeroUI Pro licensing
- HeroUI Pro Terms
商店#
- Apple Developer Program
- Apple Developer Program enrollment
- Apple upcoming requirements
- Apple App Review Guidelines
- Apple Developer Program License Agreement
- Apple 第三方 SDK 要求
- Apple Privacy manifest
- Apple 账号删除要求
- Apple:启用 CloudKit
- Apple:配置 iCloud services
- Apple:CloudKit schema workflow
- Apple:CloudKit JS
- Apple:iOS capability 会员矩阵
- Apple:为 App ID 启用 capability
- Google Play Console 注册
- Google Play 新个人账号设备验证
- 新个人账号测试要求
- Google Play target API requirements
游戏、3D 与 AR#
- React Native 性能
- React Native Skia 游戏教程
- React Native Skia Atlas
- Apple AR Quick Look
- Apple RoomPlan
- Google Scene Viewer
- ARCore Depth
- Unity AR overview
- Unity as a Library