Expo Go 与开发构建
上手 Expo 的第一个"架构级"问题不是代码怎么写,而是你的 JS 跑在哪个 App 壳里。2026 年这个问题比看上去沉重得多:Expo Go——那个当年"零构建秒上手"的招牌——已被官方逐步收窄成教学工具(单 SDK 绑定、强制登录、加载限制、商店审核滞后),而 development build 成了生产应用的默认答案。本章把三种开发环境摆成一个阶梯,给出 development build 的实操全流程、连上 dev server 的三条网络路径,以及 SDK 58 新增的 launch URL 统一契约。关键事实截至 2026-10-20 / SDK 58 实测核验;商店版 Expo Go 的上架状态以周为单位变化,引用前务必复核官方 changelog。
1. 三种开发环境的阶梯模型
把三层环境想成"租房—装修好的自购房—自建房屋":
- Expo Go(第一层,教学/起步):官方预制的通用 App 壳,从应用商店下载。它烧录了固定的一批原生模块、只对应一个 SDK 版本;你的 JS bundle 由 dev server 远程喂给它运行。壳本身装完就不可改——这决定了它的能力天花板(见第 3 节)。
- development build(第二层,生产推荐的起步方式):你自己的 App 壳 = 你的原生工程(CNG 生成)+
expo-dev-client库。加载模型和 Expo Go 完全一样(原生壳 + dev server 供 JS),但原生侧由你的 app.json 和构建时 node_modules 的 autolinking 全集决定,想用哪个原生库、想配什么 bundle id / 图标 / 权限,都是你自己的 App。官方文档的定位一句话:a development build is essentially your own version of Expo Go。 - 本地原生构建(第三层,完全掌控):
npx expo run:ios/run:android(或expo prebuild后直接开 Xcode / Android Studio)。严格说run:*就是第二层"本地出包"的方式,把第三层单列出来,指的是把生成的ios/、android/目录当一等公民对待的状态:要断点调试原生代码、改AppDelegate、写内联原生模块、排查链接器报错时,你就是干这个的。原生模块开发回看09-Expo-Modules与原生扩展.md。
三者的核心对比:
| 维度 | Expo Go | development build | 本地原生工程(run:* + prebuild) |
|---|---|---|---|
| 装起来要花什么 | 商店下载,零构建 | 出一次包(本地或云端) | 同 dev build,另需 Xcode/AS |
| 原生库范围 | 仅官方烧录的固定集合 | 任意 npm 原生库(构建时确定) | 任意,含手写原生代码 |
| 改图标/名称/splash | 不能测试 | 可以 | 可以 |
| 远程 push | 不支持(未内置) | 正常 | 正常 |
| App Links / Universal Links | 不能 | 可以 | 可以 |
| SDK 版本 | 每个商店版绑一个 SDK | 跟项目走 | 跟项目走 |
| 终端 + App 同账号登录 | 需要(iOS 真机,2026-09 起) | 不需要 | 不需要 |
| 适合 | 学习、教学、快速演示 | 生产应用日常开发 | 调试原生、完全掌控构建链 |
决策树(截至 2026-10-20):
只是在学 React Native / 写一次性 demo?
├─ 是 → Expo Go(模拟器版免登录、不限 SDK 组合,教学首选)
└─ 否,目标是上架的生产应用 → development build
├─ 本机有 Xcode(macOS) / Android Studio,愿意自己出包 → npx expo run:ios / run:android
├─ Windows/Linux 要出 iOS 包,或团队要统一产物 → eas build --profile development(见 06-EAS构建.md)
└─ 需要改原生代码 / 断点调试原生 / 手写模块 → run:* 生成的 ios/ android/ 目录直接开 IDE官方在 development builds FAQ 里对 Expo Go 的定性很直白:“It’s limited and not useful for building production-grade projects, so most apps will convert to using development builds.” 这不是客套,2026 年的三连收紧(第 2 节)之后,把它当生产环境的路基本被堵死了。
2. Expo Go 现状:2026 年的全面收窄
2.1 官方口径:教学工具
SDK 58 changelog 的 Expo Go 一节原话:“The Expo Go app is our tool for getting started quickly; it’s an educational tool to help you learn to build on mobile. If you are past that stage and want to build and ship an app, we encourage you to migrate your project to using a development build.” 这段话从 2026-05 的公开信开始,几乎每次 SDK 发布都会重申一遍。docs FAQ 里给 Expo Go 的标签是 “playground app for students and learners”、troubleshooting 页写的是 “a learning environment and sandbox”。读旧教程时要意识到:2023 年前后"用 Expo Go 一直写到上架前"的打法,官方口径已经明确不鼓励。
2.2 单 SDK 政策
每个 Expo Go 构建只包含一个 SDK 版本,项目 package.json 里 expo 依赖的版本必须与它一致,否则加载时报错:
Project is incompatible with this version of Expo Go商店里的新版 Expo Go 只支持当前最新(或次新)SDK。58 changelog 直接说了:“The new Expo Go will support only SDK 58, so upgrade your project to keep using Expo Go from the stores."——商店版 SDK 58 上架后,SDK 57 的项目用商店版 Expo Go 就打不开了,要么升级项目,要么用第 2.6 节的旁路装对应版本。
2.3 商店滞后是常态(截至 2026-10-20 的现实)
- SDK 58 于 2026-10-15 发布,changelog 注明 “Expo Go for SDK 58 on the Apple App Store and Google Play Store will be released within a few weeks”——截至 2026-10-20 尚未上架。
- 在这之前,
npx create-expo-app默认创建的仍是 SDK 57 项目。想直接起 SDK 58 项目:npx create-expo-app --template default@sdk-58,并且官方明说配套要用 development build。 - 滞后的根因是 App Store 审核。2026-05 的公开信里,SDK 55 版 Expo Go “as of May 4th, is still waiting for approval on the Apple App Store”,且给不出时间表;同期商店里可用的是 SDK 54 版。历史上"SDK 发布了、商店版还没过审"已经反复发生,商店版常年落后项目端一个到两个 SDK。
注意:docs 的 expo-go-version-mismatch 排错页(2026-09-03 修订)写着商店版"停在 SDK 54”,而同日的 login changelog 已宣布 App Store 更新到支持 SDK 57——两者矛盾,说明这类页面本身就追不上商店节奏。以官方 changelog 和 Expo Go 启动页的报错信息为准,别把任何一篇 docs 的"当前商店版本号"当长期事实抄。
2.4 2026-09-03 起:真机强制登录
从 2026-09 更新起,在 iPhone 真机上用商店版 Expo Go 跑开发项目,终端(npx expo login)和 App 内(右上角头像)必须登录同一个 Expo 账号。要点:
- 适用范围窄但很关键:仅最新版 iOS App、仅真机、仅 dev server 模式;Android 设备/模拟器、iOS 模拟器版本不受影响,官方表态 Android 后续跟进;加载已发布的更新(published updates)不受影响。
- development build 不需要登录——这是从开发体验上绕开登录问题最干净的路。
- 报错只在设备端显示,终端里 dev server 一声不吭照常运行;没登录或两边账号不一致时,Expo Go 的错误屏会明确告诉你缺哪一边。用
npx expo whoami查终端身份。 - Expo CLI 每次被请求项目时实时读凭据,服务端运行中补登录即可重试;但换账号要重启 dev server(账号在进程内缓存)。
- 顺带一提,Android 版 Expo Go 登录虽不强制,但登录后会自动列出局域网内正在跑的 dev server,还能直接加载你发布过的项目——教学场景建议学员也登录。
2.5 2026-05-12 起:加载限制
Expo Go 加载项目的能力被砍了两刀(Brent Vatne 的 changelog 公告):
- 在 Expo Go 里点开 EAS Update 链接时,只能加载你自己或你所在组织发布的更新——别人分享的更新链接加载不了。
- Hermes 字节码(HBC)bundle 只允许来自 EAS Update;自托管更新服务必须提供纯 JS bundle。
背景不难理解:Expo Go 曾经被用作免费 App 分发载体和钓鱼工具,这两条把"白嫖 Expo Go 做生产分发"的路彻底堵死。官方对"给团队预览"给出的三条正规路:商店内部测试轨道(Google Play internal testing / TestFlight,见 react-native/11-构建与发布.md)、EAS Build 内部/临时分发,或者 development build 配合 EAS Update(见 07-EAS发布与热更新.md)。
2.6 绕开商店滞后的四种办法
| 办法 | 命令/入口 | 适用设备 | 前置条件 |
|---|---|---|---|
| 官网选版本 | expo.dev/go 按 SDK + 平台挑 | Android 真机/模拟器、iOS 模拟器 | 无 |
expo-go CLI |
npx expo-go download android latest、npx expo-go url ios 56 |
同上 | 无 |
| 自建 Expo Go | eas go 构建后走 TestFlight |
iPhone 真机 | Apple Developer Program 会员;docs 口径覆盖 SDK 54+(SDK 53 及更早无法在 iPhone 真机装旧版 Expo Go) |
| 建项目时选版本 | SDK 56 起 create-expo-app 会让你选"兼容商店版 Expo Go"还是"最新 SDK";也可 --template default@sdk-55 钉版本 |
— | 无 |
但这些都是治标。每次大版本 changelog 都在指同一条治本的路:生产应用迁移到 development build。
3. Expo Go 的能力边界
3.1 内置了什么
- 一方 SDK 模块:docs 各模块页的 platforms 字段会标
expo-go(页面上显示 “Included in Expo Go”),没标的就不在。 - 二三方精选库:SDK 58 docs 专页 “Third-party libraries supported in Expo Go” 列了约 20 个:
@react-native-async-storage/async-storage、@react-native-community/datetimepicker、netinfo、slider、masked-view、picker、segmented-control、@shopify/flash-list、@shopify/react-native-skia、@stripe/stripe-react-native、react-native-gesture-handler、react-native-keyboard-controller、react-native-maps、react-native-pager-view、react-native-reanimated、react-native-safe-area-context、react-native-screens、react-native-svg、react-native-view-shot、react-native-webview。官方原话:“You may use any library of your choice with development builds.” - 顺带两个纠偏点:
react-native-webview如今已内置(旧教程里"WebView 必须上 dev build"的说法过时了);但"模块内置"不等于“模块所有功能内置”——例如 expo-sqlite 的 SQLCipher 加密在 Expo Go 里不可用,细粒度限制看各模块页的说明。
3.2 确定做不到的事
FAQ 专门列了"在 Expo Go 里什么不可能、为什么",理解"为什么"才能判断什么时候必须换环境:
- 用不在内置清单里的原生库。FAQ 用 react-native-webview(内置)对比 react-native-firebase(不内置)讲机制:JS bundle 热进 Expo Go 毫无问题,但 JS 一调用不存在的原生实现立刻报错。“There is no way to get native code into the Expo Go app unless it was already included in the bundle that was uploaded to the app stores."——原生代码进不去,物理法则。
- 测试图标/应用名/splash 屏的改动。这些资产烧在原生壳里;Expo Go 展示的 splash 只是"图标 +纯色背景"的 dev 模拟,
SplashScreen.setOptions的动画都没法测。 - 远程 push 通知。当前 docs 措辞:“You must use a development build to use push notifications since the capability is not built into Expo Go."(in-app/本地通知在 Expo Go 可用)。旧时代"iOS Expo Go 只能收到发给本机的推送"那套 device push 说法已成历史,现在的口径干脆是不支持。push 服务要绑定你自己的 APNs/FCM 证书,这是它进不了通用壳的根本原因。
- App Links / Universal Links。需要原生工程里烧入关联域名,同理做不到。
- 跨 SDK 打开项目。见 2.2 的单 SDK 政策。
- 任何自定义原生代码(config plugin 产出之外的手写 Swift/Kotlin、inline modules)——直接回
09-Expo-Modules与原生扩展.md,从写的那一刻起你就在 development build 的世界里了。
4. development build:生产应用的默认开发环境
4.1 心智模型:原生壳与 JS 的分工
FAQ 里这段值得背下来:装到设备上的是原生 App(Expo Go 是预制的,dev build 是你编译的);expo start 跑的是 JS bundler;生产包里 JS 打进 main.js 随 App 分发,开发时 JS 从你的机器实时加载。只有烧进原生壳里的 API 可用——这一句同时解释了"Expo Go 装不了第三方原生库"和"dev build 加原生库要重打包”。
4.2 出包全流程
# 1. 装 dev client(SDK 58 实测版本 expo-dev-client@58.0.14)
npx expo install expo-dev-client
# 2A. 本地出包(需要 Android Studio / Xcode,不需要 Expo 账号)
npx expo run:android # 默认装到运行中的模拟器
npx expo run:ios # 默认装到 iOS 模拟器
npx expo run:android --device # USB 真机,弹窗允许 USB 调试
npx expo run:ios --device # iPhone 真机:需开启开发者模式,且 app 配置里
# 有唯一的 ios.bundleIdentifier
# 2B. EAS 云端出包(本机零原生工具链,Windows/Linux 也能出 iOS 模拟器包)
eas build --platform android --profile development
eas build --platform ios --profile development
# 2C. 走 EAS 的流程与签名,但编译在自己机器上
eas build --platform ios --profile development --local
# 3. 装好之后启动 JS 侧
npx expo start几个关键点:
run:*会在没有原生目录时自动执行prebuild(CNG 的常态:ios/、android/是生成物,不要手改也不要进 git,见01-核心概念.md)。- 装了
expo-dev-client之后,npx expo start自动以 dev build 为目标,不再提示扫码进 Expo Go;不用 dev-client 库的裸 dev build 才需要npx expo start --dev-client。 - EAS 云端的
developmentprofile 必须带developmentClient: true,完整 eas.json 见06-EAS构建.md,最小片段:
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": { "simulator": true }
}
}
}ios.simulator: true 只用于模拟器包(simulator 包与真机包互不兼容,两类都要就分开建 profile)。云端构建完成后 CLI 会提示按 Y 直接装到模拟器,或扫码装到真机;产物也可以随时从 expo.dev 控制台或用 Expo Orbit 重装(Orbit 在装构建、切设备这类杂务上很省心)。--local 构建的产物是本地的 .apk / .app 归档,拖进模拟器即可。签名话题在 06 章;这里只需记住两条:上 iPhone 真机的 dev build,走 EAS 必须有付费 Apple Developer 账号;不想付费的唯一路是 macOS 本机 run:ios --device(docs 原话:本地构建是 paid account 之外把 dev build 装进 iPhone 的唯一方式)。
4.3 Launcher 屏幕、dev menu 与 Tools 浮标
dev build 冷启动后进入 launcher 屏幕:Home 标签列出局域网内检测到的 dev server(或直接输入 URL / 扫码),Updates/Extensions 标签对接 EAS Update。官方给的直通条件是:局域网能发现 bundler,或 Expo CLI 与 dev build 登录了同一账号;否则就扫 npx expo start 打的 QR。dev menu 用摇晃手机 / Cmd+D(iOS)/ Ctrl+D(Android)呼出;悬浮 **Tools 按钮(FAB)**是常驻入口,点它等价于呼出 dev menu,还能切换不同的 development server——同事机器上跑的 server、发布出去的 update,都在这个壳里切换。expo-dev-client 暴露的运行时 API 就这几个:DevClient.openMenu()/closeMenu()/hideMenu()、setToolsButtonVisible(visible)、registerDevMenuItems(items)(往菜单里加自定义条目)。
4.4 什么时候必须重建 dev build
“用了 dev build 之后加原生库还要不要重装 App”——要。dev build 的原生集合是构建时由 autolinking 定下的,不是包含一切的万能壳。docs 给出的重建触发清单(官方口径三条):
- 安装或更新任何含原生代码的库(一方 expo-* 或三方 autolinking 库都算);
- 修改 app.json 里影响原生工程的字段(图标、名称、bundle id、scheme、增删 config plugin 等,配置分层见
03-应用配置与插件.md); - 升级 Expo SDK(升级工作流和重建时机详见
11-升级与诊断.md,changelog 原文:“If you use development builds with expo-dev-client: create a new development build after upgrading.")。
拿不准改动是否触及原生,跑 npx expo prebuild --clean 看生成的原生目录 diff,再重新出包。反过来,纯 JS/TS 的改动永远不需要重建——保存即 fast refresh,这点和 Expo Go 时代的心智完全一致。顺带一提:SDK 58 起 fingerprint 默认 preset 改为 balanced(改 version 号、动 node_modules 内部文件不再产生新指纹),那是 EAS Update 侧"哪些变更需要发原生新版本"的判定(07-EAS发布与热更新.md),不要与上面"要不要重建 dev build"混为一谈。
4.5 构建期默认值:expo-dev-client 插件
expo-dev-client 自带 config plugin(用法见 03-应用配置与插件.md),这些选项是构建期默认值,改动需要重新出包:
| 属性 | 默认 | 作用 |
|---|---|---|
launchMode |
"most-recent" |
启动直接进上次打开的项目(连不上退回 launcher);"launcher" 则总是进 launcher 屏幕 |
addGeneratedScheme |
true |
注册 exp+{slug} URL scheme(第 6 节的主角) |
defaultLaunchURL |
— | 直接进这个 URL,不经过 launcher;Android 模拟器常用 http://10.0.2.2:8081 |
toolsButton |
true |
是否默认显示悬浮 Tools 按钮 |
skipOnboarding |
false |
跳过安装后首次启动的 onboarding 屏 |
showMenuAtLaunch |
true |
启动即弹 dev menu |
embeddedBundle |
false |
开启后 launcher 出现"Load embedded bundle"选项 |
{
"expo": {
"plugins": [
["expo-dev-client", { "launchMode": "most-recent", "skipOnboarding": true }]
]
}
}构建期默认值之外,单次启动还能用 launch URL 参数覆盖——第 6 节整节讲这个契约。
4.6 热更新体验:dev build 下 fast refresh 与 EAS Update
开发体验没有降级:Metro、fast refresh、日志/错误屏与 Expo Go 完全同款;SDK 58 还修了两个高频痛点(代理/远程机连接、双实例串台,见第 5 节)。EAS Update 的预览通道在 dev build 里更顺:装了 expo-updates 后,launcher 的 Extensions 标签登录 Expo 账号即可列出各 branch 的最新发布更新,点 Open 就在 dev build 里跑发布包;也可以从 EAS 控制台扫码/经 Orbit 打开。runtimeVersion 边界与发布流程在 07-EAS发布与热更新.md;SDK 大版本升级后原生壳过时,记得重建 dev build(11-升级与诊断.md)。
5. 连上 dev server 的三条网络路径
npx expo start 的连接模式在终端按 t 循环切换,或启动时用 flag 固定:
| 模式 | 原理 | 适用 | 限制 |
|---|---|---|---|
| Local(localhost) | 设备访问自身 localhost:8081 |
iOS 模拟器;Android 模拟器经 10.0.2.2 映射宿主机 |
其他设备不可达 |
| LAN | 设备访问开发机局域网 IP | 手机与电脑同一 Wi-Fi 的真机调试,日常首选 | 路由器隔离/guest 网络直接判死刑 |
| Tunnel | 流量经中继出公网再回来 | 公网、公司网络、手机热点等一切隔离场景 | 最慢,且 58 起要登录 |
官方在 start-developing 页明确警告:Tunnel 模式下 reload 比 LAN/Local 慢得多,能不用就不用;公司网络必须用 tunnel 时,建议改用本机模拟器/模拟器跑开发,真机留到 LAN 可用的环境。
5.1 SDK 58 的新隧道
58 把隧道实现换成了自研的 @expo/ws-tunnel(2026-10-20 npm 实测 latest 2.0.1):连接更快、更稳、不再依赖 @expo/ngrok。代价是需要 expo login(隧道服务端归 Expo 运营,账号是准入凭证)。旧路仍留着:npx expo start --tunnel ngrok 走遗留的 ngrok 实现。
5.2 58 修复的连接顽疾
- 代理/远程机场景:dev build 启动时拉取的 manifest 过去会把 bundle URL、debugger 域名写成 dev server 自认为的地址(经代理或 Bonjour 发现时常常不可达)。58 起客户端支持时 manifest 改用相对 URL,跑在反向代理后、远程机器上的 dev server,dev build 现在能可靠连上。
- 双 bare 实例串台:
RCT_METRO_PORT曾是编译期宏、预编译的 React Native 里冻结在 8081——两个不带 dev-client 的 bare App 同时开着会互相应答对方的 reload 指令。58 起 prebuild 把端口写进Info.plist的RCTMetroPort键,App 启动时读取,各找各的 server。 - 端口选择:Expo CLI 选端口更一致,非交互场景占用时自动滚到下一个空闲端口而不是直接报错。
5.3 常见故障排查
- iPhone 真机 Expo Go 打不开、终端无报错——十有八九是没登录或两边账号不一致(2.4 节);先
npx expo whoami核对。 - 公司防火墙/手机热点连不上 LAN——切 tunnel(58 起先
expo login);嫌 reload 慢就回本机模拟器。 - Android 模拟器连不上——用
http://10.0.2.2:8081(或 dev-client 插件的android.defaultLaunchURL写死它)。 - dev build 的 launcher 列表里看不到同事的 server——对方与你的设备不在同一广播域,让对方给你
exp://或更新 URL 扫码,或都登录同一组织账号。 - 老版 Expo Go(商店滞后期的经典问题)报版本不兼容——按 2.6 节装对应 SDK 版本,或干脆升级项目。
6. Launch URL:58 的统一契约
6.1 为什么需要契约
以前两种壳两套格式:dev build 走 {scheme}://expo-development-client/?url={manifestUrl},Expo Go 走 exp://ip:port。launcher 参数(url=、disableFab=1…)没有保留前缀,会和 App 自己的深链查询参数撞名——你的业务页面恰好也有 ?url= 时,链接到底给谁解析成了玄学。SDK 58 让 development build 与 Expo Go 共用一套契约:任何 host、任何 scheme,凡以 __expo_ 开头的查询参数都是 launcher 指令,由 launcher 处理并在交给 App 前移除,永不进入你的深链处理或路由参数。
6.2 四个参数
| 参数 | 语义 |
|---|---|
__expo_url |
URL 编码后要加载的项目/更新 URL(manifest URL) |
__expo_disable_onboarding=1 |
跳过安装后首启的 onboarding 屏 |
__expo_disable_fab=1 |
本次启动隐藏悬浮 Tools 按钮 |
__expo_disable_auto_launch=1 |
本次启动不自动弹出 dev menu |
后三个只影响本次启动,不改持久设置——之后用户正常打开 App,菜单和按钮照常回来。Expo Go 的 exp:// URL 同样接受这些参数,例如 exp://192.168.1.10:8081?__expo_disable_auto_launch=1。npx expo start 现在打印的新形式(scheme 默认为 exp+{slug}):
exp+app-slug://?__expo_url=https%3A%2F%2Fu.expo.dev%2F<projectId>%2F%3Fchannel-name%3Dmain需要程序化生成扫码页时,把 appScheme 与 url 拼到 https://qr.expo.dev/development-client 就能拿到 SVG 二维码。
6.3 旧形式兼容与一个语义陷阱
{scheme}://expo-development-client/?url=... 及其旁边的 disableOnboarding=1 / disableFab=1 / disableAutoLaunch=1 在 58 仍正常工作——存量链接、二维码、脚本都不用改(SDK 57 及更早只认旧形式)。但注意语义差异:旧版 disableFab/disableAutoLaunch 会写持久设置,__expo_disable_* 只对当次生效。给 CI/测试写脚本时统一用新参数,别新旧混用。
6.4 CI、端到端测试与 agent 场景
EXPO_NO_DEV_MENU=1 npx expo start设置后,该会话里 Expo CLI 生成的每个 launch URL、二维码、重定向都自动带上三个 __expo_disable_* 参数——onboarding、FAB、自动弹菜单全部安静掉。Expo CLI 在非交互环境(无 TTY,典型如 CI/agent 循环)默认开启,EXPO_NO_DEV_MENU=0 可显式关掉。配套 @expo/agent-cli 的 dev/smoke 命令也是这套思路(见 02-命令行操作.md)。
6.5 App 侧:expo-linking 与路由的配合
expo-linking与 Expo Router 忽略全部保留参数,__expo_*不会出现在路由的 search params 里;从 launch URL 解析出"App 实际该路由到的 URL"用expo-linking新导出的unwrapDevLaunchURL()。Linking.parse()同期修掉查询值二次解码的老毛病,百分号编码的%、+、@原样通过。- 两个 dev build 专属限制:App 业务深链(
myscheme://path/to/screen)要求项目已经在 dev build 里打开,冷启动带业务深链暂不支持;自己设计的深链路径里避开expo-development-client这个保留 path。
7. 实战建议
新项目直接用 development build 起步是 2026 年的默认正确姿势,理由有三:Expo Go 的单 SDK 绑定 + 商店滞后意味着"随时不可用”(58 首发期官方自己都让直接开 SDK 58 项目的人配 dev build);加载限制与登录要求使它连"给同事看个效果"都费劲;而生产应用迟早撞上第 3 节那堵墙,晚上切换不如早上切换。最小成本路径就四步:npx create-expo-app → npx expo install expo-dev-client → 有 Xcode/AS 本机 npx expo run:ios(或 run:android),没有就 eas build --profile development → 之后日常 npx expo start。第一次多花的那十几分钟构建,买回的是"加任何库都不用换环境"的确定性。
团队里可以双轨:给新人上手、内部教学、快速验证想法,用 Expo Go 仍然是最快路径——推荐模拟器版(免登录、可经 expo.dev/go 选 SDK 版本),避开真机登录和商店版本的两重不确定性;正式功能开发一律 dev build。给 QA/产品预览不要发 Expo Go 链接(2.5 节的加载限制会拦),走 dev build + EAS Update 或测试轨道分发。
迁移时机信号(中一条就该切了):要装的第一个原生库不在内置清单里;要做远程 push;要配 App Links/Universal Links;要真实测试图标/splash/启动动画;项目 SDK 与商店版 Expo Go 对不上且你不想等审核。迁移本身很薄:装 dev-client、出一次 dev build、从此 expo start 自动指向它——JS 代码一行不用改,这正说明 Expo Go 从来只是个壳,不是运行时。