Skip to content
命令行操作

命令行操作

Expo 的一切开发动作都收口在一个命令家族上:npx expo <子命令>。这一章讲清楚本地 CLI 的心智模型、start / install / prebuild / run / config / export 等每个命令的关键参数与 58 的行为变化,最后收录 SDK 58 新出的面向 agent 的命令(command:ps、@expo/agent-cli)和环境变量速查。基线为 Expo SDK 58(expo 58.0.16,2026-10-20 npm 实测),凡"SDK 58 起"的断言均对照官方 changelog 或 @expo/cli 源码核实。


1. 心智模型:npx expo 是本地 CLI,全局 expo-cli 已死

先纠一个流传最广的旧习惯:不要再 npm i -g expo-cli。全局 expo-cli 是 SDK 45 之前的时代产物,早已废弃(官方 docs 首页明确写"expo-cli (global install) is deprecated. Use npx expo")。今天的 CLI 是随 expo 包一起安装的 @expo/cli,在项目目录里用 npx expo 调用——它与你项目里的 SDK 版本严格配对,这正是它比全局安装更可靠的原因:SDK 58 项目里跑的永远是 58 的 CLI。

npx expo 不带子命令时等价于 npx expo start(源码里 defaultCmd = 'start')。跑 npx expo -h 能看到的命令分组大致是:

Commands
  start, export
  run:ios, run:android, prebuild
  install, customize, config, serve
  login, logout, whoami, register

Agent Commands
  command, command:ps, command:events

顺带一提,很多旧教程里的命令在本地 CLI 里已经不存在,npx expo <旧命令> 会被迁移提示接住。@expo/cli 源码里维护了一张迁移映射表(截至 2026-10-20 的 main 分支实测),常用的几个:

旧命令(全局 expo-cli 时代) 现在用什么
expo eject npx expo prebuild(eject 这个概念本身已废弃,见 01-核心概念.md)
expo build:ios / build:android eas build -p ios / -p android
expo publish eas update
expo doctor npx expo-doctor
expo upgrade 官方升级流程(见 11-升级与诊断.md)
expo credentials:manager eas credentials

值得特别注意:expo devices 和 expo diagnostics 在本地 CLI 的命令表里不存在(从 @expo/cli 源码 src/index.ts 的命令映射核实,2026-10-20)。它们是旧全局 expo-cli 的命令,敲 npx expo devices 会被当成"以 devices 为项目目录启动 dev server",症状是莫名其妙的"找不到项目"。列设备用 Terminal UI 的 Shift+A / Shift+I 或 npx expo run:android --device;诊断项目用 npx expo-doctor。

还有一个命令不出现在任何文档里但真实存在:expo export:embed,它是 run:ios/android 在生产构建时内嵌 bundle 的内部命令,日常不用管。

2. expo start:开发服务器

npx expo start 起一个 Metro 开发服务器(默认 http://localhost:8081),终端里出现带二维码的 Terminal UI。它是你一天按得最多的命令。

2.1 暴露方式:localhost / lan / tunnel

设备要能访问到你的服务器,三种网络拓扑:

npx expo start --localhost   # 仅本机(模拟器用这个,最稳)
npx expo start --lan         # 局域网 IP(真机默认走这个)
npx expo start --tunnel      # 公网隧道(公司防火墙/VPN 下的救命通道)

默认是 LAN。SDK 58 起 --tunnel 的实现换掉了:新隧道基于 @expo/ws-tunnel,不再依赖 @expo/ngrok,连得更快、掉线更少,但必须先 npx expo login 登录 Expo 账号才能用。要退回旧 ngrok 隧道(比如不想登录的场合),显式传 --tunnel ngrok。有个细节:docs 的 CLI 参考页在 2026-10-16 的版本里仍写着"先 npm i -g @expo/ngrok“的旧流程,属于文档滞后,以 changelog 为准。

隧道相关还值得知道两件事:EXPO_PACKAGER_PROXY_URL 可以强制指定对外 URL(反向代理/远程开发机场景);--tunnel 与 --offline 互斥,隧道必须有网。

2.2 目标运行时与常用参数

npx expo start --dev-client          # 强制按 development build 打开(装了 expo-dev-client 时本来就是默认)
npx expo start --go                  # 强制按 Expo Go 打开
npx expo start -c                    # --clear:清 Metro 缓存,改 metro.config.js/babel 后必按
npx expo start --port 8082           # 换端口;--port 0 表示自动取第一个空闲端口
npx expo start --offline             # 断网开发,跳过所有网络探测
npx expo start --web                 # 起完直接开浏览器;--ios / --android 同理自动打开对应端
npx expo start --no-dev --minify     # 用生产模式打包来复现"只有 release 才有的 bug"

--dev-client 和 --go 同时传会直接报错,二者互斥。启动时目标探测的逻辑是:装了 expo-dev-client 就按开发构建,否则按 Expo Go;运行中按 S 可以随时切换(见下文),API routes 与 Expo Go/开发构建的差异见 05-Expo-Go与开发构建.md。

端口在 SDK 58 有两个行为变化(changelog 原文,#47771 / #48300 / #48098):

  • 非交互模式下端口被占用时自动递增到下一个空闲端口,不再直接报错退出。这是 agent/CI 友好的一大改进——以前 CI=1 npx expo start 撞上 8081 被占就整条流水线红掉。
  • 端口不再靠编译期宏 RCT_METRO_PORT(预编译的 React Native 里它被冻死在 8081,两个裸应用同时跑会互相响应对方的 reload 指令),prebuild 现在把端口写进 Info.plist 的 RCTMetroPort 键,App 启动时读取。

2.3 Terminal UI 快捷键

服务器跑起来后,终端本身就是控制台:

按键 作用
A / Shift+A 打开 Android 设备 / 从列表选择设备或模拟器
I / Shift+I 打开 iOS 模拟器 / 选择模拟器
W 浏览器打开 Web 端
R 让所有已连接设备重载
S 在 Expo Go 与 development build 之间切换启动目标
M / Shift+M 打开开发菜单 / 更多动作(性能监视、元素检查器等)
J 打开 React Native DevTools(Hermes)
O 在编辑器中打开项目(EDITOR / EXPO_EDITOR 环境变量控制)
E 把服务器 URL 重新渲染成二维码
? 全部快捷键

2.4 启动 URL 契约与开发菜单开关(SDK 58 新)

SDK 58 统一了 Expo Go 与 development build 的启动 URL:以 __expo_ 开头的查询参数一律是启动器指令,由壳层消费并从 URL 中剥离,永远不会漏进你的路由参数(expo-linking / Expo Router 对其免疫)。常用的四个:

  • __expo_url:要加载的项目/更新地址(URL 编码)。npx expo start 现在打印 {scheme}://?__expo_url={manifestUrl},旧格式 {scheme}://expo-development-client/?url=... 继续兼容。
  • __expo_disable_onboarding=1:跳过装完后的首次引导页。
  • __expo_disable_fab=1:本次启动隐藏打开开发菜单的悬浮按钮。
  • __expo_disable_auto_launch=1:本次启动不自动弹开发菜单。

CI、端到端测试、agent 循环直接用环境变量 EXPO_NO_DEV_MENU=1:让 CLI 生成的每个启动 URL/二维码自动带上上面三个 disable 参数。CLI 检测到非交互环境(无 TTY、CI)时默认就是 1,用 EXPO_NO_DEV_MENU=0 可以反悔。顺带纠偏:--no-dev-menu 并不是 expo start 的参数(start 的参数解析源码里没有这个 flag),这件事 58 时代只靠 EXPO_NO_DEV_MENU 控制;旧文档/旧脚本里的写法要改。

另外 dev server 暴露了 /_expo/open HTTP 端点:GET /_expo/open?platform=ios 返回 JSON 形式的深链(可穿过隧道),POST 等价于在终端按 I/A/W。远程 agent 或云预览服务靠它触发启动,不必解析二维码。

2.5 .expo 目录

首次 start 后项目根会多出 .expo/(内含 devices.json、settings.json),是本机专属状态,模板默认已 gitignore。隧道 URL 的随机前缀熵也缓存在这里,想"重置"隧道就删这个目录。

3. expo install:永远别裸 npm i 装原生相关依赖

Why 先行:React Native 的 npm 包没有向后兼容承诺——某个 react-native-xxx 只有在配合特定 RN 版本时才编译得过、跑得起来。Expo 每个 SDK 版本就是一张锁死的版本矩阵(SDK 58 = RN 0.88 + React 19.3.0,npm 实测)。npx expo install <pkg> 是 npm install 的直接替换品,区别在于它会按你当前 SDK 的矩阵挑版本、写对范围符(~/exact 因包而异),而裸 npm i 只会给你 latest——在 Expo 项目里 latest 几乎总是错的。

npx expo install expo-camera                 # 装 SDK 匹配的 expo 模块
npx expo install react-native-reanimated expo-sms   # 一次多个
npx expo install react-native-svg -- -D      # 剩余参数透传给底层包管理器

3.1 –check 与 –fix:版本错位自查

npx expo install --check        # 列出与当前 SDK 不匹配的包,交互确认后修复;CI 下发现错位即以非零退出
npx expo install --fix          # 无条件把错位依赖直接修到兼容版本(不提问)
npx expo install expo-sms react-native --check   # 只校验指定包

--check 的设计意图是给 CI 当"不可变校验"用的:CI=1 npx expo install --check 一旦有包过期就失败。--fix 则是升级 SDK 后的标准动作(完整流程见 11-升级与诊断.md)。确实想用非推荐版本时,在 package.json 里配 expo.install.exclude: ["some-package"] 把它移出校验。

包管理器自动跟随 lockfile(package-lock.json→npm、pnpm-lock.yaml→pnpm、yarn.lock→yarn、bun.lock*→bun),也可用 --npm / --pnpm / --yarn / --bun 强制。npx expo add 是 install 的别名。

4. expo prebuild:从 app 配置生成原生工程

npx expo prebuild 是 CNG(Continuous Native Generation)的执行器:拿 expo-template-bare-minimum 模板 + 跑 config plugins,生成 android/ 与 ios/ 目录。“生成了原生目录 ≠ eject”,它被视为可再生的构建产物——生成物要不要进 git、怎么管原生改动,是 09-Expo-Modules与原生扩展.md 的主题,这里只讲命令本身。

npx expo prebuild                  # 双端生成(已存在的目录会增量叠加修改)
npx expo prebuild -p ios           # 只生成 iOS(--platform android 同理)
npx expo prebuild --clean          # 先删除现有 android/ios 再全新生成
npx expo prebuild --no-install     # 跳过依赖安装与 pod install(快速试生成是否报错)
npx expo prebuild --skip-dependency-update react-native,react   # 生成时不动这两个包
npx expo prebuild -t ./my-template.tgz   # 自定义模板(不推荐:@expo/prebuild-config 对模板有未文档化假设)

三个实战提醒:

  • --clean 会删掉你对原生目录的手工改动,检测到 git 工作区有未提交更改时会警告;CI 里跳过警告用 EXPO_NO_GIT_STATUS=1。日常习惯应是"改配置和 plugin,不碰生成物”,一旦要 --clean 就说明 plugin 没写对(这是 09 章反复强调的点)。
  • 不带 --clean 的重跑是把 plugin 修改"叠加"在既有文件上,非幂等的 plugin(正则改文件那类)叠加会出问题,官方建议多数场景直接 --clean。
  • 图标/闪屏在这一步按 app config 的 icon/splash 字段渲染生成(依赖 Sharp;设 EXPO_IMAGE_UTILS_NO_SHARP=1 会退回慢得多的 Jimp)。

5. expo run:ios / run:android:本地编译原生应用

npx expo run:ios                # 首次:自动 prebuild -p ios → pod install → xcodebuild → 装进模拟器
npx expo run:android            # 首次:自动 prebuild -p android → gradle assembleDebug → adb install

没有对应原生目录时会自动先跑一次 npx expo prebuild(只生成本平台)。首次构建耗时要有预期:iOS 端 CocoaPods 安装加全量编译,M 系 Mac 上 10-30 分钟很正常,Windows 跑 Android 首次 Gradle 下载依赖同样漫长;之后的增量构建才是分钟级以内。工具链硬要求:run:ios 只能在 macOS + 完整 Xcode 上跑,run:android 需要 Android Studio/SDK 与 JDK(详见 00-概述与安装.md);不想伺候本地工具链就用 EAS 云构建(见 06-EAS构建.md)。

跨平台参数(docs “Compiling” 一节核实):

参数 说明
-d, --device [设备名或ID] 装到具体设备;不带值从列表选;--device generic 只编译不装(产物路径打印在末尾,可配 -o ./build 拷出来)
--no-install 跳过 npm 依赖安装(iOS 同时跳过 pod install)
--no-build-cache 清原生缓存后编译(iOS 是 DerivedData),适合排查玄学编译错误
--no-bundler 编译完不拉起 dev server
-p, --port <port> dev server 端口(仅开发构建有意义)
--binary <path> 跳过编译,直接把现成产物装到设备
-o, --output <dir> 把编译产物复制到指定目录(CI 友好)

平台专属:Android 用 --variant debug / release / debugOptimized(debugOptimized 是 SDK 54 起的新变体:C++ 按 release 优化但仍可调试;EAS 里对应 :app:assembleDebugOptimized),自定义 flavor 配 --variant freeDebug --app-id dev.expo.myapp.free;iOS 用 --scheme 选 scheme、--configuration Release 出生产配置——注意两种生产本地产物都不会自动签好可上架的证书,上架包交给 EAS Submit。

npx expo run:android --variant release
npx expo run:ios --configuration Release --device generic --output ./build

SDK 58 的一个隐性变化:Android 模板的 release 构建默认开启 R8(gradle.properties 里 android.enableMinifyInReleaseBuilds=true),反射密集的库需要补 ProGuard keep 规则,用 expo-build-properties 可关(细节见 11-升级与诊断.md)。

6. expo config:把最终 app 配置打印出来

config plugin 行为不符合预期时,第一件该做的事是看"算完之后"的配置:

npx expo config                 # 评估 app.json / app.config.ts,打印运行时生效的清单
npx expo config --type prebuild # 生成原生工程用的配置(含异步 plugin 修改,唯一不可序列化的形态)
npx expo config --type introspect  # 只列 plugin 在内存里对 Info.plist / AndroidManifest.xml 等的修改
npx expo config --full --json # 全量配置 + JSON 输出(重定向给 diff/脚本用)

排 plugin 问题的套路:某个键在 app.json 里写了但没生效——npx expo config 对比 --type prebuild 的输出,差值就是被某个 plugin 改写/吞掉的;怀疑某个 plugin 动了 plist/manifest,用 --type introspect 逐项核对。深入调试手法(断点进 plugin)见 03-应用配置与插件.md。

7. expo export 与 expo serve:离线打包与本地验证

npx expo export                       # 生产模式:Metro 打 bundle、剥 __DEV__、拷资源到 dist/
npx expo export --platform web        # 只出 Web(ios/android/all 同理,默认 all)
npx expo export --dev                 # 开发模式 bundle(不压缩、保留 __DEV__),仅调试用
npx expo export --output-dir ./out    # 换输出目录(默认 dist)
npx expo export -c                    # 先清 Metro 缓存
npx expo export --no-minify           # 跳过压缩(分析包体积构成时用)
npx expo export --no-bytecode         # 跳过 Hermes 字节码生成——只用于体积分析,千万别把 UTF-8 bundle 发到设备上
npx expo export --api-only            # 不出静态 HTML,只生成 server 代码(配合 Router API routes)

产物 dist/ 是纯静态目录,可直接托管;Web 侧 static 与 server 两种 output 的行为差异见 10-Web与托管.md。npx expo serve 用来在本地把某个 export 产物伺服起来做验证(参数只有 --port),SDK 58 起它对"用 expo export --dev 打出的、包含 server 代码的导出"直接拒绝启动(changelog #50522)——生产服务器代码不该混 dev 导出是官方给的理由,遇到报错就重新 npx expo export(不带 --dev)。

eas update 和 run:* 生产构建内部都会调 export,日常手跑的场景不多:验证离线包行为、自建静态托管、CI 产物审查。

8. expo customize:生成配置文件(不生成图标)

npx expo customize        # 交互式选择要落盘的文件

它把本来在内存里生成的默认配置文件写到项目里供你改:babel.config.js、metro.config.js、tsconfig.json(webpack.config.js 选项已随 Expo Webpack 废弃)。用途很实际:要用 Expo CLI 之外的工具(裸 react-native CLI、jest 定制、Turborepo)打包时,这些文件必须存在。

顺带纠偏:旧全局 expo-cli 时代 expo customize 能生成图标/闪屏资产,本地 CLI 的 customize 只做上面这几件事。图标闪屏的正确姿势是把源图指到 app config 的 assets/(icon/splash/adaptiveIcon 字段),由 prebuild/构建管线生成各密度产物(配置写法见 03-应用配置与插件.md)。

9. 账号:login / logout / whoami / register

npx expo login          # SDK 58 起默认拉起浏览器完成授权(#46832)
npx expo login --no-browser          # 回到旧式终端输入
npx expo login --username u --password p   # 非浏览器流也可直接传参(CI 用 EAS_ACCESS_TOKEN 更好)
npx expo whoami         # 当前登录身份
npx expo logout
npx expo register       # 注册账号

这套凭据与 EAS CLI 共享。为什么本地开发要登录:manifest 要用你的凭据签名(相当于给 OTA 配 HTTPS);SDK 58 的新隧道强制要求登录;2026-09-03 起 iOS 真机 Expo Go 要求 App 与 CLI 登录同一账号,不一致的排查见 05-Expo-Go与开发构建.md。

10. expo-doctor:项目健康检查

npx expo-doctor 是独立 npm 包(与本地 CLI 解耦,2026-10-20 实测 latest 1.21.5),一条命令体检依赖版本矩阵、app 配置、lockfile、Metro 配置等已知坑。npx expo doctor(旧写法)会被迁移表引到这里。升级 SDK 后的标准动作之一,检查项全列表与用法展开在 11-升级与诊断.md,这里只需记住:遇到"装完跑不起来"先跑一遍 doctor,再开始搜索报错。

11. SDK 58 新增:面向 agent 的命令与 @expo/agent-cli

注意 本节两组能力官方都标注 experimental:command:ps / command:events 的输出格式官方明说"会随时间调整";@expo/agent-cli 处于活跃开发中。写生产 CI 脚本前想清楚升级成本。

11.1 expo command:ps / command:events

SDK 58 的 CLI 给每个命令运行记录"会话事件",并提供两个查询命令,让 agent(或被 AI 代理操作的你)不用解析终端里的花哨 UI 就能掌握全局状态(changelog “Agentic development with Expo CLIs” 一节):

npx expo command:ps --active --json          # 机器可读地列出正在跑的 Expo 进程(含 dev server)
npx expo command:events <session-id> --since 5m   # 回放某会话的结构化事件日志

官方原话是"这取代了旧的 debug 日志系统"——连带效应:58 起你再用 EXPO_DEBUG=1 调试 CLI,看到的输出和以前不一样了。help 文本也按 agent 可读性重做过。这两个命令同样有 --help 可查过滤参数。

11.2 @expo/agent-cli

一个架在 Expo CLI、EAS CLI、expo-doctor 之上的实验性 CLI(npm 实测 latest 1.0.20,2026-10-19 发布;包名 @expo/agent-cli,另有别名 expo-agent-cli;仓库 github.com/expo/expo-agent-cli),设计目标是"agent 不必理解 Expo 全家桶的边界"。命令一览:

命令 干什么
npx @expo/agent-cli@latest agents:setup 入口:给 agent 环境(Claude Code 等)配置好这套 CLI
status 不启动应用即可回答"这个项目和 Expo Go 兼不兼容"
dev 一条命令把应用跑起来——该构建时构建,自动挑最快的启动路径,输出里带 next steps
smoke dev + 截图 + 收尾停止:验证"这台机器上真的能开始开发"
skills:sync 把 node_modules 里随包共置的 agent skills 同步进项目

它本质是 Expo CLI 的编排层、可随时回退,changelog 建议的用法就是"让 agent 用它替代直接调 Expo CLI"。人机协作细节见 05-Expo-Go与开发构建.md 与 12-最佳实践与陷阱.md。

12. 环境变量速查

CLI 行为大量由环境变量控制(表身核实自 docs CLI 参考页,2026-10-16 版本;SDK 58 行为补充自 changelog):

变量 作用
EXPO_DEBUG 等价 DEBUG=expo:*,打开 CLI 调试日志。58 起底层换成了命令事件系统,输出形态有变
CI 非交互模式:禁用交互提示、可选项自动跳过、必选项缺失直接失败(CI=1 npx expo install --check 是经典流水线写法)
EXPO_NO_TELEMETRY 关闭匿名使用统计
EXPO_NO_DEV_MENU SDK 58+:给所有启动 URL 附加开发菜单禁用参数;非交互时默认 1,传 0 反悔
EXPO_OFFLINE 跳过所有 CLI 网络请求(弱网提效)
EXPO_NO_CACHE 禁用 CLI 全局缓存(配置 schema、模板、模拟器版 Expo Go 二进制)
EXPO_NO_GIT_STATUS prebuild --clean 前不做 git 脏区警告(CI 自动跳过)
EXPO_PACKAGER_PROXY_URL 强制对外服务 URL(代理/远程机场景)
EXPO_NO_TYPESCRIPT_SETUP 阻止 start 时自动配置 TypeScript
EXPO_NO_WEB_SETUP 阻止 CLI 自动装 web 依赖(react-dom / react-native-web)
EXPO_NO_DEPENDENCY_VALIDATION 关掉 install/start 时的内置依赖版本校验(自管矩阵时用)
EXPO_NO_DOTENV 全局禁用 .env 加载
EXPO_NO_QR_CODE 终端不渲染二维码
EXPO_ATLAS 导出时收集 Metro bundle 分析数据(SDK 53+)
EDITOR / EXPO_EDITOR 按 O 时打开哪个编辑器(后者优先级高)
EXPO_IMAGE_UTILS_NO_SHARP 图标生成禁用 Sharp、退回 Jimp(慢,仅无法装 Sharp 时用)
EXPO_PROFILE CLI 自身耗时剖析(不 profile 你的应用)
HTTP_PROXY CLI 全部网络请求走代理(undici 约定)

.env 文件与 EXPO_PUBLIC_* 的加载规则是另一个话题,见 03-应用配置与插件.md。另外 SDK 58 起 NODE_ENV 由 CLI 按命令强制覆盖为 development/production,不再尊重外部传值——这对 .env 加载影响很大,专门展开见 11-升级与诊断.md。

13. 命令速查总表

命令 一句话 高频参数
npx expo start 起 dev server(默认命令) --localhost/--lan/--tunnel、--dev-client、-c、--port
npx expo install <pkg> 按 SDK 版本矩阵装依赖 --fix、--check、--npm/--pnpm/--yarn/--bun
npx expo prebuild 生成 android/ios 原生工程 --clean、-p ios、--no-install
npx expo run:ios 本地编译并运行 iOS --device、--configuration Release、--scheme
npx expo run:android 本地编译并运行 Android --device、--variant release/debugOptimized
npx expo config 打印评估后的 app 配置 --type prebuild/introspect、--full、--json
npx expo export 打包产物到 dist/ --platform、--dev、--output-dir、--no-bytecode
npx expo serve 本地伺服 export 产物 --port(58 起拒绝含 server 代码的 --dev 导出)
npx expo customize 落盘 babel/metro/tsconfig 配置 (交互式选择)
npx expo lint 配 ESLint 并检查代码 --fix、--ext
npx expo login/whoami 账号凭据(与 EAS CLI 共享) --no-browser、--username/--password
npx expo-doctor 项目健康检查(独立包) --help
npx expo command:ps / command:events 进程与事件查询(experimental) --active --json、--since
npx @expo/agent-cli dev/smoke/status agent 编排入口(experimental) agents:setup 先行

实战建议

  • 把 npx expo install 设成肌肉记忆。团队里只要有人用裸 npm i 装过一个 react-native-* 包,--fix 就该出现在 PR 检查里;这条纪律能消灭八成的"红屏版本错位"。
  • 网络疑难的排查顺序:--localhost(模拟器)→ --lan(同网段真机)→ --tunnel(跨网络)。隧道慢是物理规律,能不用就不用;公司防火墙下 Windows 真机往往只有隧道一条路,提前让同事 expo login。
  • 非交互环境记住 CI=1 的副作用:端口自动递增、EXPO_NO_DEV_MENU 默认开、install --check 严格失败。agent 脚本与本地手跑的观感差异基本都来自这三个。
  • plugin 调试三板斧:expo config 看基线 → --type prebuild 看生成输入 → --type introspect 看 plist/manifest 修改。三板斧解决不了再去 03 章学断点调 plugin。
  • 旧教程里看到 expo eject、expo build:*、expo publish、expo devices、全局装 expo-cli 的字样,一律当作 2022 年前的材料处理。