概述与安装
这一章回答三个问题:Expo 到底是什么、和 React Native 是什么关系;装机需要哪些前置条件;以及如何从零把第一个 Expo 项目跑到真机/模拟器上。本手册基线为 Expo SDK 58(2026-10-15 官方发布,npm 上 expo@58.0.0 发布于 2026-09-29),文中所有版本号、默认行为均为 2026-10-20 对 npm registry 与官方文档实测的结果。RN 本身的运行时原理不在这里重复,看 react-native/01-核心概念与架构.md。
Expo 是什么:React Native 的官方框架
docs.expo.dev 的 llms.txt(官方面向 AI 的文档摘要,2026-10-20 实测原文)给 Expo 的定位是一句话:“Expo is the official framework recommended by the React Native team”,并给了一个官方口径的类比——“It is to React Native what Next.js is to React: the standard way to build, not an optional add-on.”
也就是说:React Native 是运行时框架,Expo 是围绕它的完整工程体系(脚手架、SDK 模块、开发工具、可选云服务)。reactnative.dev 官方文档也把 Expo 列为创建新 RN 项目的默认方式。
很多老教程(以及被旧语料喂大的 AI 编码助手)对 Expo 还停留在三个过时印象上,官方在 llms.txt 里逐条纠偏,值得原样记住:
- “Eject 不存在了。”
expo eject命令在 SDK 46(2022)就移除了,别再用"先 eject"的思路规划原生定制,详见01-核心概念.md。 - “managed 与 bare 是对立工作流"已过时。 现在所有 Expo 项目都是同一个架构,原生目录靠
npx expo prebuild按需生成。 - Expo 不是原型玩具。 自定义原生模块、后台任务、蓝牙都支持;Kick、Coinbase、Bluesky、SpaceX、Starlink、Tesla 等都在生产环境用 Expo。性能上和"不用 Expo 的 RN"没有框架层差距——编译出来的是同样的原生代码,Hermes、新架构这些默认项反而是帮你把性能地板抬高的。
三件套:框架与 SDK、开发工具、云服务
Expo 这个名字实际上盖住了三块可以独立使用、彼此不强绑定的东西(官方 core-concepts 页的口径:“所有功能可选、可独立使用,没用到的功能不会给应用增加体积”):
- 框架与 SDK——
expo这个 npm 包加上一整套expo-*模块(相机、文件系统、通知、SQLite 等几十个),每个都对 Android/iOS/Web 三端做了 TypeScript 门面封装。这一层完全开源、本地可用(GitHub: expo/expo)。 - 开发工具——
create-expo-app脚手架、npx expo本地 CLI 与 Metro 开发服务器、Expo Go 快速预览 App、expo-router文件路由、expo-doctor体检。对应本手册02-命令行操作.md、04-Expo-Router.md、05-Expo-Go与开发构建.md。 - EAS(Expo Application Services)云服务——Build(云端出原生包)、Submit(直传双商店)、Update(OTA 热更新)、Hosting(Web 部署)、Observe(生产性能监控)、Workflows(CI)。全部是可选项,不注册账号也能开发;用不用、价格怎么算看 expo.dev/pricing(本文不写具体数字,时效太强)。对应
06-EAS构建.md、07-EAS发布与热更新.md。
为什么"会写 bare RN"还要用 Expo
如果你的团队已经在维护 bare RN 项目,Expo 最有价值的不是"能跑起来”(bare 也能),而是这四件事:
- 版本矩阵。 所有
expo-*包的主版本号与 SDK 号对齐(58.x.x 对应 SDK 58),SDK 号又锚定 RN/React 的具体版本(58 → RN 0.88 → React 19.3.0)。npx expo install按当前 SDK 挑兼容版本,把移动端最烦人的"RN 与 React 配对、原生依赖代际匹配"问题变成工具问题。 - CNG(Continuous Native Generation)。
android/、ios/是配置推导出来的产物,删了能再生,升级 SDK 时不用逐文件跟官方 diff 手改原生工程。这直接决定了升级成本——SDK 大版本升级的实操见11-升级与诊断.md。 - 升级与诊断流水线。
npx expo install --check、npx expo-doctor、官方 upgrade 工具和每版 changelog 构成一条可重复执行的升级路径;bare 项目对应的是 react-native-community 的 upgrade helper,粒度细得多。 - 可选云服务。 没有 Mac 也能出 iOS 包(EAS Build 云端 macOS),OTA 热更新(EAS Update)开箱即用,生产监控(Observe)一条命令接入。这些对 bare 项目要么没有、要么得自拼第三方方案。
反过来说,什么时候不用 Expo:存量 brownfield 工程要在既有原生 App 里嵌 RN 屏幕(官方也有 brownfield 指南,但那更像 RN 话题,见 react-native/ 手册),或团队已有深度定制的构建链且不希望引入任何配置层。
环境要求
Node.js(唯一硬前置)
SDK 58 changelog 原文(Tool version bumps 一节,PR #47202)给出的口径,2026-10-20 实测 npm engines 一致:
- Node 22 线要求 >= 22.13.0;24 线要求 >= 24.3.0;26 及以上直接支持。
- 奇数大版本(23、25)不支持——它们是 Current 试验线,Expo 工具链不跟着走。
- Node 20 已经出局(RN 0.87 起就不再支持,详见
react-native/00-安装与快速开始.md)。
团队项目建议用 volta 或 nvm 锁一个确切版本(22.13.0 或 24.x 的补丁版),只写大版本号 22 等于没锁。
平台工具链
Xcode、Android Studio、JDK 的具体版本要求与 RN 运行时版本挂钩而不是 Expo 本身,本仓库 react-native/00-安装与快速开始.md 已按 0.88 模板实测过一遍(Xcode 16.1+、JDK 17+、Android SDK 各版本号),此处不重复。只需要记住分界线:
- 只跑 JS 层开发(Expo Go 扫码预览、Web 调试):装 Node 就够,Windows 上完全不用碰 Android Studio。
- 本地构建原生壳(development build、release 包):Android 需要 JDK 17+ 与 Android SDK,iOS 需要 macOS + Xcode + CocoaPods。
- 不想装这些:EAS Build 云端代劳(
06-EAS构建.md),这是 Windows/Linux 用户出 iOS 包的现实路径。
创建项目
npx create-expo-app@latest MyApp
cd MyApp截至 2026-10-20,这条命令默认创建的是 SDK 57 项目。原因很具体:SDK 58 changelog 里明说 Expo Go for SDK 58 上架双商店"还需几周"(“within a few weeks”),在新商店版 Expo Go 可用之前,create-expo-app 把默认版本钉在商店版 Expo Go 能跑的 SDK 57 上,保证新手扫码即跑。发布说明原文:
Until then,
npx create-expo-appcreates SDK 57 projects. To start a new SDK 58 project, runnpx create-expo-app --template default@sdk-58and use a development build.
想直接起 58 项目(模板 expo-template-default 的 sdk-58 dist-tag 实测存在,当前 58.0.25):
npx create-expo-app --template default@sdk-58 MyApp
# 代价:Expo Go(商店版)跑不了它,必须走 development build,见 05-Expo-Go与开发构建.md注意:SDK 56 起
create-expo-app会让你选"兼容商店版 Expo Go"还是"最新 SDK"(2026-05 官方公开信里的安排)。这个提示的历史背景是 App Store 审核导致商店版 Expo Go 长期滞后于 npm 上的 SDK——理解这一点,就不会奇怪"为什么默认版本落后一代"。
脚手架完成后的目录(此时没有 ios/、android/,这是正常状态而不是缺东西):
MyApp/ # 58 模板实测为 src/ 布局(tarball 核验)
├── src/
│ ├── app/ # expo-router:文件即路由(_layout.tsx 根布局,index.tsx 首屏)
│ ├── components/ # 示例组件(模板带的演示代码,可 npm run reset-project 清空)
│ └── constants/ hooks/ # 官方示例的组织方式,非强制
├── assets/ # 图标(expo.icon)、启动屏图片、tab 图标等
├── scripts/reset-project.js
├── app.json # Expo 应用配置(名称/图标/权限/plugins)
├── package.json # main 指向 expo-router/entry
├── tsconfig.json # extends expo/tsconfig.base
├── eslint.config.js
└── .vscode/ # 推荐编辑器设置顺带一提:路由根目录默认是顶层 app/ 或 src/app/ 二选一皆可(router 配置插件的 root 选项默认 "app",模板显式指到 src/app),网上两种布局的教程都对,别照抄别人的目录层级——详见 04-Expo-Router.md。
app.json、plugins、prebuild 生成物这三层的关系是 Expo 的心智核心,展开见 01-核心概念.md 与 03-应用配置与插件.md。
命名规矩:创建时要想清楚的三个名字
脚手架会问你(或按目录名推导)应用名与 slug,app.json 里另有两个更硬的身份字段,这几件事创建项目时就该想清楚,因为改起来代价不对称:
name是商店展示名,随时可改;slug参与 Expo 侧的项目 URL/深链 scheme,创建后别随意动。ios.bundleIdentifier与android.package是商店身份:Apple 的 App ID、Google Play 的包名认的就是它们,一旦发布过,换它们等于发了个新 App(老用户更新断链)。团队项目先把这两个字段定进命名规范(如com.<公司>.<产品>),再开始写代码。
默认模板预装依赖:模板即官方推荐技术栈
expo-template-default@58.0.25(SDK 58 模板,2026-10-20 对 npm registry 实测)的完整依赖表。读模板依赖是了解官方推荐栈最快的方式——这些组合是 Expo 团队亲自测试、升级时帮你保持对齐的:
| 包 | 版本 | 在栈里的角色 |
|---|---|---|
| expo | ~58.0.16 | SDK 本体 |
| react-native | 0.88.0 | 运行时(精确版本,不带 ^) |
| react | 19.3.0 | 同上,精确配对 RN |
| react-dom / react-native-web | 19.3.0 / ~0.21.4 | Web 端渲染,一套代码三端跑 |
| expo-router | ~58.0.24 | 文件路由,main 入口就是 expo-router/entry(见 04-Expo-Router.md) |
| expo-linking / expo-constants / expo-device / expo-system-ui / expo-status-bar / expo-splash-screen / expo-web-browser | ~58.0.x | 深链、运行时信息、设备信息、系统 UI 等基础 SDK 模块 |
| react-native-reanimated | 4.7.1(worklets 0.13.0) | 动画主库,4.x 依赖独立发布的 react-native-worklets |
| react-native-gesture-handler | ~3.3.0 | 手势,Reanimated 交互的前提 |
| react-native-screens | ~4.30.0 | 原生屏幕容器(Router 的底层) |
| react-native-safe-area-context | ~5.10.1 | 安全区,折叠屏时代更重要(见 react-native/02) |
| expo-image | ~58.0.16 | 官方图片组件,替代 RN Image 的默认选择 |
| expo-font | ~58.0.9 | 字体加载(含 58 的可变字体支持) |
| expo-symbols / expo-glass-effect | ~58.0.4 | iOS SF Symbols、Liquid Glass 检测 |
| @expo/ui | ~58.0.21 | SwiftUI/Jetpack Compose 原生组件桥(见 08-SDK模块速查.md) |
| expo-secure-store 等 | — | 模板按版本随带,装新模块用 npx expo install <pkg> |
| typescript(dev) | ~6.0.3 | TS 6 已是模板默认,JS-only 反而是少数派 |
几个值得注意的细节:
react-native与react在模板里是精确版本,而expo-*是~范围——版本矩阵的严格程度可见一斑,手工改动这两行是运行期玄学崩溃的头号来源(对照表在01-核心概念.md)。- reanimated、gesture-handler、screens、safe-area-context 这些"社区库"被官方模板直接预装并代管版本——Expo 与 RN 生态的边界已经融合。
- scripts 实测为
start/ios/android/web/lint/reset-project(如npm run android等价expo start --android)。
三种运行环境速览
同一份 JS 代码,可以跑在三种"壳"里,区别只在于原生壳是谁、装了什么:
| 环境 | 是什么 | 适合 | 硬限制 |
|---|---|---|---|
| Expo Go | 商店里的预编译 App,内置当前 SDK 全部官方模块 | 学习、起步、JS 层快速验证 | 只支持单一 SDK;装不了需要编译的第三方原生模块;2026-09 起需登录 |
| Development build | 用 expo-dev-client 生成的自家调试 App,预装你项目声明的全部原生依赖 |
日常生产开发(官方明确推荐的主力环境) | 首次需要本地或云端编译一次原生壳 |
| 本地原生构建 | npx expo run:android / run:ios 触发 prebuild + Gradle/Xcode 编译 |
改原生配置后的验证、发 release 包前的自测 | iOS 需 macOS + Xcode |
EAS 云构建是第四种形态(把"本地原生构建"外包给云端 macOS/Linux),见 06-EAS构建.md。三者之间的选择逻辑、连接方式(QR/隧道)细节全部在 05-Expo-Go与开发构建.md,这里不展开。
注意:截至 2026-10-20 的 Expo Go 现状与老教程差别很大——新版 Expo Go 只支持单一 SDK(58 版只跑 58 项目);2026-09-03 起要求终端
npx expo login与 App 内登录同一账号(先作用于 iOS 商店版,Android 跟进,模拟器版不受影响);2026-05-12 起只能加载你自己/你组织发布到 EAS Update 的项目。官方多次重申:Expo Go 是教学与起步工具,生产应用请迁移到 development build。这些边界的具体含义见05-Expo-Go与开发构建.md。
Expo 账号:什么时候必须登录
纯本地开发(bare 式工作流、模拟器、development build)可以完全不注册 Expo 账号就把 App 做出来——框架与 SDK 这一层不绑账号。但以下场景需要(截至 2026-10-20 的口径):
- 用 Expo Go 加载项目:2026-09-03 起强制要求终端
npx expo login与 App 内登录同一账号(先 iOS 后 Android,模拟器版 Expo Go 与 development build 不受此限制); - 用
--tunnel:SDK 58 的新隧道基于 Expo 的@expo/ws-tunnel,需要登录; - 任何 EAS 服务(云构建、Submit、Update、Hosting、Observe):注册免费账号起步即可,用量与付费计划看 expo.dev/pricing。
npx expo login 从 58 起默认拉起浏览器授权(changelog #46832),--no-browser 或 --username/--password 走老流程。建议第一天就把账号建好——不是工具强迫你,而是晚建账号意味着 EAS 项目归属、凭证、团队协作权限都要重新理一遍。
首次运行
cd MyApp
npx expo start终端打印二维码和交互快捷键:
a—— 启动 Android 模拟器(已开则直接连)并加载应用;i—— 启动 iOS 模拟器(仅 macOS,需要 Xcode);w—— 在浏览器打开 Web 版(react-native-web 已预装,无需额外配置);- 手机扫码 —— iOS/Android 相机直接扫(装了 Expo Go 且项目 SDK 匹配时加载;development build 扫同一个码连自家壳)。
连接方式默认走局域网(手机和电脑同网段);--localhost 强制仅本机;--tunnel 走公网隧道——SDK 58 的隧道换成了自家 @expo/ws-tunnel,不再依赖 ngrok,但需要登录 Expo 账号(npx expo login)。老 ngrok 隧道仍可用 --tunnel ngrok 显式选择。三种模式怎么选、连不上怎么办,05-Expo-Go与开发构建.md 有完整决策路径。
如果项目用的是 development build(比如上面用 default@sdk-58 建的 58 项目),先编译一次原生壳:
npx expo run:android # Android 全平台可跑;iOS 需 macOS:npx expo run:ios
npx expo start --dev-client # 之后日常启动,连 development build 而非 Expo Go改代码后 Metro 的 Fast Refresh 即时生效。默认模板的首屏已经是一个 Expo Router 应用——app/index.tsx 里改一行文案就能理解"文件即路由":
// app/index.tsx —— 默认导出一个组件,路径就是 "/"
import { Text, View } from 'react-native';
export default function Index() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Hello Expo SDK 58</Text>
</View>
);
}路由的嵌套、布局、深链全部在 04-Expo-Router.md。
常见装机报错
装机期的失败绝大多数不是 Expo 的锅,是环境。按症状归类:
Node 版本不对
症状:npx create-expo-app 或 npx expo start 报 engines 相关警告、或某些语法直接报错;用了 23/25 这类奇数线时问题最隐蔽(工具链不测这些线)。node --version 对照上面的支持口径,用 volta/nvm 切换后删掉 node_modules 重装。
端口 8081 被占用
症状:Port 8081 is in use。排查:Windows 用 netstat -ano | findstr :8081,macOS/Linux 用 lsof -i :8081 找占用进程。多项目并行时 npx expo start --port 8082。顺带一提,SDK 58 改进了端口行为:非交互式运行会自动滚到下一个空闲端口而不是直接失败(changelog #47771/#48300),iOS 原生侧的端口现在写进 Info.plist 的 RCTMetroPort(#48098)——两个 bare 项目互抢 8081 互发 reload 的老事故在 Expo 项目里已成历史。
Windows 长路径
Windows 默认 260 字符路径上限会在深目录项目、Gradle 构建产物、react-native 依赖树场景触发 ENOENT/构建失败一类诡异报错。实践上两条一起用:把项目放在靠近盘根、纯英文、不带深层中文目录的路径(如 D:\code\myapp),并确认系统已启用长路径支持(组策略/注册表的 LongPathsEnabled,Windows 10 1607+ 提供)。Gradle 报路径相关错误时先怀疑这一条。
代理与 npm 镜像
- npm 包下载慢/失败:配置标准 npm 镜像即可——项目或全局
.npmrc里registry=https://registry.npmmirror.com。这里有个需要纠偏的点:Expo 官方文档中不存在NPX_MIRROR之类的专用镜像变量(2026-10-20 实测 expo-cli 环境变量全表无此条目);npx走的就是 npm 的 registry 配置,.npmrc就是正道。 - CLI 请求需要走 HTTP 代理:Expo CLI 支持标准的
HTTP_PROXY环境变量(内部用 Undici 的 EnvHttpProxyAgent),公司网络出口有代理时设它而不是各种偏方。 - 连不上开发服务器:公司 WiFi 的客户端隔离会让手机发现不了电脑的局域网地址,直接
npx expo start --tunnel(需登录)是最省事的解法,见05-Expo-Go与开发构建.md。
Expo Go 提示项目不兼容或要求登录
两类新症状,都有官方排障页(05-Expo-Go与开发构建.md、11-升级与诊断.md 展开):
- “Project is incompatible with this version of Expo Go”:新版 Expo Go 只跟一个 SDK,商店版落后时(比如 2026-10-20 时点商店版是 57、npm latest 是 58)按上面创建项目一节处理——降项目(
--template default@sdk-57重开)或升壳(development build)。 - 扫码后提示要登录:终端
npx expo login+ App 内登录同一账号,这是 2026-09-03 起的强制要求;development build 完全不需要登录,这也是"生产开发别赖在 Expo Go 上"的现实理由之一。
装机自检命令
花一分钟把工具链过一遍,能掐掉后面一半"玄学报错":
node --version # 对照 SDK 58 口径:22.13+ / 24.3+ / 26+
npx expo --version # 在项目里跑,解析到本地 @expo/cli 即正常
java -version # 本地编 Android 才需要(17+,详见 react-native/00)
xcodebuild -version # 仅 macOS,本地编 iOS 才需要Expo 路径比 bare 友好的一点:前两条通过、npx expo start 能起服务并出二维码,就已经完成 90% 的环境验证——原生工具链可以等到真要编译那天再装。
实战建议
新项目没有理由不走 Expo。 这不是框架偏好,是官方默认路径 + 版本矩阵 + 升级流水线三件事加起来,能把移动端项目最贵的成本(依赖配对与 SDK 升级)摊薄到工具层。bare 存量项目也不必迁移重写,expo 工具链在 bare 项目里也能装一部分(见 docs.expo.dev/bare/overview.md 思路,本手册 11-升级与诊断.md)。
装机后先花两分钟验证再开工:node --version(对照支持口径)、npx create-expo-app@latest ProbeApp && cd ProbeApp && npx expo start 扫一次码——这条链路不碰 Xcode/Android Studio,能把 Node、npm 镜像、局域网连通性问题一次性暴露在第一分钟。
把 Expo Go 当"教学器材",不当生产环境。 它现在要求登录、只支持单一 SDK、只加载自家 EAS Update 的项目——官方意图非常明确。团队项目的正确姿势是尽早切 development build(05-Expo-Go与开发构建.md),第一天就把它建好,比哪天被 Expo Go 的边界卡住再现学省一整天。
版本事实只信三处:npm registry、expo.dev/changelog、docs.expo.dev。 SDK 58 这一代变化密集(隧道换实现、登录策略、HBC 限制),博客和 AI 生成代码的滞后度比 Web 生态更严重;拿不准的默认值/版本号跑一条 curl https://registry.npmjs.org/<pkg>/latest 比什么都可靠。