Expo EAS Build 与 Submit 完全指南:iOS 与 Android 应用上架部署实战(2026)

Expo EAS Build 与 EAS Submit 的实战工作流:凭证管理、三套构建 profile、自动上架 App Store 与 Google Play,以及 OTA 更新与回滚策略。

Expo EAS Build 与 Submit 上架指南 (2026)

更新于:2026 年 6 月 8 日

Expo EAS Build 是 Expo 官方提供的云端编译服务,用于把 React Native 工程打包成可上架 App Store 与 Google Play 的二进制文件,而 EAS Submit 负责把这些产物自动推送到 App Store Connect 与 Google Play Console。我用 EAS 流水线上线过六款应用,从两人小作坊到日活百万的产品都有。下面这套流程,是我现在每个新项目第一周就会落地的「标准动作」,覆盖凭证、构建配置、提交、OTA 更新与回滚。

  • EAS Build 在 2026 年仍然是 Expo 与裸 React Native 项目最稳的云端构建方案,免费档每月 30 次构建,付费档支持优先队列与 M2 Pro 加速。
  • iOS 与 Android 凭证应交由 eas credentials 托管,别把 .p12 或 keystore 直接提交到 Git。
  • developmentpreviewproduction 三套 profile 区分调试、TestFlight 灰度与正式上架,再配合 channel 字段精确控制 OTA 推送。
  • EAS Submit 一次性配置后即可全自动上架,iOS 端必须先把 ASC API Key 上传到 EAS。
  • EAS Update 适合修 JS/资源 bug,但任何原生依赖变更仍然必须重新跑 EAS Build。
  • 使用 runtime version policy fingerprint,是新架构项目最安全的版本兼容策略。

什么是 EAS Build 与 EAS Submit

EAS(Expo Application Services)是 Expo 团队提供的一组云端开发者服务,其中 EAS Build 负责把 React Native 工程编译为 .ipa.aab/.apk,EAS Submit 负责自动提交到 App Store Connect 与 Google Play Console。它的卖点是不再需要自维护一台插着 Apple ID 的 Mac mini:Apple Silicon 构建机由 Expo 托管,凭证由 EAS 加密保管,日志可以在浏览器里直接查看。

从 2026 年 4 月起,EAS Build 的免费档为每月 30 次构建,Production 计划提供优先队列、M2 Pro 加速与更大的缓存配额。详细额度可参考 Expo 官方定价页。对于裸 React Native 项目(没有用 Expo 模板创建的那种),EAS Build 同样可用,只要工程里包含 ios/android/ 原生目录即可。这一点很多团队仍然误以为 EAS 只服务 managed workflow,其实早就不是了。

关键区别一句话总结:Build 给你新的二进制,Submit 把它推上商店,Update 则是绕过商店给已上架版本推送 JS/资源补丁。三者通常一起使用,但各自有独立的计费与触发逻辑。

安装 EAS CLI 并初始化项目

所有操作开始前,先在本地装好 EAS CLI 并登录。我推荐用 npx 而不是全局安装,这样多项目并存时不会出现版本错位,CI 镜像也能始终拿到最新的 patch 版本。

# 1. 临时调用最新版 CLI(推荐)
npx eas-cli@latest --version

# 2. 登录 Expo 账号(企业账号请先在 Web 控制台开 Organization)
npx eas-cli login

# 3. 在项目根目录初始化
cd my-rn-app
npx eas-cli build:configure

# 输出会询问:Which platforms? -> All
# 它会生成 eas.json 并在 app.json 写入 projectId

build:configure 会自动检测项目类型。如果是 Expo Managed 项目,它只写 eas.json;如果是裸 RN,它还会顺手校正 android/app/build.gradle 里的版本号读取方式。我个人会在这一步把项目接入 React Native 新架构(Fabric 与 TurboModules),因为 EAS Build 对 Fabric 的支持已经非常成熟,新项目没有理由还从 Old Architecture 起步。

另外建议在仓库里加一份 .easignore,语法与 .gitignore 一致。它能阻止本地的 node_modules、Xcode DerivedData、Android build/ 目录被上传到云端,通常能把上传体积从 200 MB 砍到 20 MB,提速可观。这一点官方文档藏得比较深,但实际收益很明显。

构建 Profile 与 eas.json 详解

EAS 的灵魂在 eas.json:同一份代码通过不同 profile 可以生成完全不同的产物。我固定用三套 profile,十几个项目下来都没失手过。

{
  "cli": { "version": ">= 12.0.0", "appVersionSource": "remote" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "channel": "development",
      "ios": { "simulator": true },
      "android": { "buildType": "apk" }
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview",
      "ios": { "resourceClass": "m-medium" },
      "android": { "buildType": "apk" }
    },
    "production": {
      "channel": "production",
      "autoIncrement": true,
      "ios": { "resourceClass": "m-medium" },
      "android": { "buildType": "app-bundle" }
    }
  },
  "submit": {
    "production": {
      "ios": {
        "ascAppId": "1234567890",
        "appleTeamId": "ABCDE12345"
      },
      "android": {
        "serviceAccountKeyPath": "./secrets/play-service-account.json",
        "track": "internal"
      }
    }
  }
}

几个容易踩坑的字段:

  • appVersionSource: "remote":让 EAS 服务端管理 buildNumberversionCode,本地不再需要每次提交都改 app.json,对多人协作几乎是必选项。
  • distribution: "internal":产物会生成一个可扫码安装的链接,Android 直接装 APK,iOS 则需要设备 UDID 已注册到 Provisioning Profile。
  • buildType: "app-bundle":正式发版必须用 AAB,Google Play 已经禁止纯 APK 上架。
  • channel:对应 EAS Update 的推送通道,profile 与 channel 必须一一对应,否则 OTA 推送会错位。

iOS 凭证与 Provisioning Profile 实战

iOS 凭证是新手最大的拦路虎。EAS 提供了两种模式:让 EAS 全自动管理(推荐),或者手动上传 .p12。99% 情况下选自动就好。

# 交互式登录 Apple ID 并由 EAS 创建/同步凭证
npx eas-cli credentials

# 或者直接在第一次 build 时由 EAS 引导创建
npx eas-cli build --platform ios --profile production

EAS 会用你的 Apple ID 登录 App Store Connect API,自动创建 Distribution Certificate 与 Provisioning Profile,然后把私钥加密存到 EAS 服务器。整个过程不超过两分钟,关键是不再需要 Keychain 与 Xcode Organizer,这对没有 Mac 的 Windows/Linux 开发者意义重大。

如果开启了 Apple Account 的两步验证(2026 年所有开发者账号都强制开启),推荐改用 App Store Connect API Key:在 ASC 里 Users and Access → Integrations 创建一个 Admin 权限的 Key,把 .p8 文件、Key ID、Issuer ID 通过 eas credentials → "Add new App Store Connect API Key" 录入。后续 Submit 与凭证同步都不再需要交互式 2FA,CI 全自动跑也不会卡。我上一个项目就是因为没切到 API Key,凌晨自动发版被 2FA 卡了快两个小时,那种凌晨三点爬起来手动点验证码的体验,真的不想再来一次。

对于企业内部分发(In-House),需要把 distribution 字段设为 "enterprise",并在 EAS credentials 中切换到 Enterprise Program 账号。注意 Apple 在 2025 年大幅收紧了 In-House 资格审核,新申请的拒批率超过 70%,能用 TestFlight 就别走 Enterprise。

Android Keystore 与签名配置

Android 简单得多。首次运行 eas build --platform android 时 EAS 会提示生成一份新的上传 keystore,默认的 RSA 2048 + 10000 天有效期完全够用。千万记住:上传 keystore 一旦丢失,App ID 实际上就报废了。Google Play 虽然支持 Play App Signing(应用签名密钥与上传密钥分离),但前提是你能继续用原上传密钥签名 AAB。

# 备份 Android 凭证到本地(放进密码管理器或公司金库)
npx eas-cli credentials -p android

# 选择 "Download Credentials" -> "Keystore"
# 它会下载一份 .jks 与对应的密码、key alias、key password

Play App Signing 是 Google 自 2021 年起的强制要求,EAS 已经默认启用。上传 AAB 后 Google 会用平台层的应用签名密钥重新签名再分发给用户。这意味着,真正能影响安装的不是你的上传密钥,而是 Google 那把你看不到的密钥。所以一旦发版后想切换 App ID 几乎不可能,选 App ID 务必慎重。

另外一个常被忽略的点:android.versionCode 必须严格递增。如果用了 appVersionSource: "remote",EAS 会自动管理;否则建议在 CI 里跑 npx eas-cli build:version:set 把版本号显式同步,避免本地工程师覆盖远程值导致提交被 Play Console 拒。

运行第一次 EAS Build

凭证就绪后,跑一次完整构建。我习惯先用 preview profile 出一份内部测试 APK,Android 装机比 iOS 快得多,能最快发现资源缺失或 Hermes 兼容性问题。

# 提交构建任务,默认进入云端队列
npx eas-cli build --platform android --profile preview

# 查看实时日志(也可在 https://expo.dev 上看)
npx eas-cli build:list

# iOS Production 构建
npx eas-cli build --platform ios --profile production --auto-submit

--auto-submit 会在 Build 成功后立刻触发 Submit,适合 CI 自动发版。手动模式下,Build 完会得到一个产物 URL,可以下载 IPA/AAB 本地调试,或者直接进入下一步 Submit。一次典型的 production 构建,iOS 大约 12 到 18 分钟,Android 8 到 12 分钟,免费档队列高峰期可能再等 5 到 20 分钟。Production 计划下,M2 Pro 资源池构建时间可以减半。

如果你的应用依赖原生模块比较多,建议在 eas.json 的 build profile 里开启 cache.cacheDefaultPaths: true,EAS 会缓存 CocoaPods 与 Gradle 依赖,第二次起的构建提速 40% 左右。再叠加上面提到的 .easignore,整条流水线就足够清爽了。

用 EAS Submit 自动上架

Submit 是把 Build 产物推上商店的环节。第一次配置稍繁琐,之后就是一行命令的事。

iOS 端需要先在 App Store Connect 创建对应的 App 记录(填好 Bundle ID、SKU、首选语言),拿到 ASC App ID 填进 eas.jsonsubmit.production.ios.ascAppId。然后:

# 自动从最近的 production 构建中选择 IPA 提交
npx eas-cli submit -p ios --latest

# 显式指定某个 build ID
npx eas-cli submit -p ios --id 9d8c7b6a-1234-5678-9abc-def012345678

提交后 IPA 进入 App Store Connect 的处理队列,大约 10 到 30 分钟后出现在 TestFlight,审核版本则需要单独点 "Submit for Review"。EAS Submit 不会替你触发审核,这是有意为之,避免误推未完成的版本。

Android 端需要一份 Google Play Service Account JSON。具体步骤:Play Console → 设置 → API 访问 → 关联 Google Cloud 项目 → 创建服务账号 → 授予 "发布管理员" 权限 → 下载 JSON。把 JSON 放到仓库外(或加密为 EAS Secret),路径写入 submit.production.android.serviceAccountKeyPath。完整流程也可参考 Expo Submit Android 官方文档

# 推送到 Internal Testing 轨道
npx eas-cli submit -p android --latest --track internal

# 推送到 Production 轨道(直接面向所有用户)
npx eas-cli submit -p android --latest --track production

track 选项支持 internalalphabetaproduction。我的习惯是 production build 默认推 internal,人工在 Play Console 推 100% 灰度后再升 production,避免一行命令直推 100% 用户引发的事故。这条经验,是用一次崩溃率 4% 的 P0 事故换来的,那种 release 后五分钟监控就开始飙红的体验,谁经历过谁知道。

EAS Update:OTA 热更新与回滚

EAS Update 是 OTA 通道:不走商店审核,直接把新的 JS bundle、图片、字体推到已经安装应用的设备。每次冷启动,RN 运行时会从 Expo CDN 拉取最新 manifest,匹配 channel 与 runtime version 后下载补丁。整个流程类似 Web 的渐进式发布。

# 给 production 渠道推送一次更新
npx eas-cli update --branch production --message "Fix payment screen typo"

# 紧急回滚:把 channel 重新指向更早的发布
npx eas-cli channel:edit production --branch production-rollback

但 Update 不能解决一切问题,关键约束在于:原生层变更必须重新发版。换言之,只要 package.json 里改动了带原生代码的依赖(比如新增 react-native-mmkv),或者 app.json 改了 plugin 列表,就一定要跑一次完整的 EAS Build,Update 是推不出去的。

这就引出了 runtimeVersion 策略。在 2026 年,我强烈推荐用 fingerprint 策略,它会基于项目的原生指纹(原生依赖、配置插件、entitlements)自动计算 runtime version。任何会引起原生层不兼容的改动会自动产生新的 runtime version,从而避免把不兼容的 JS bundle 推给老二进制。具体配置见 EAS Update runtime versions 文档

关于发版策略的更深讨论,可以参考我之前写的 Zustand v5 + MMKV 持久化指南,里面提到 MMKV 升级时为何必须重新走 Build,而不是 Update。

常见错误排查与构建提速

整理一份过去一年我们团队 EAS 工单里最高频的几个错误:

  • iOS: "No matching profiles found":99% 是 app.jsonios.bundleIdentifier 与 ASC 上的 App ID 不一致。改完后跑 eas credentials → "Remove all" 再重试。
  • Android: "INSTALL_PARSE_FAILED_NO_CERTIFICATES":APK 被中间人改写过,或 Play App Signing 配置缺失。重新下载 EAS 提供的安装链接。
  • "Gradle build failed with exit code 137":OOM,在 eas.jsonandroid.gradleArgs-Xmx4g,或者直接升级到 m-medium 资源类。
  • "Hermes bytecode mismatch":OTA 推送的 bundle 与二进制中的 Hermes 版本不一致,通常意味着 runtimeVersion 没正确递增。改用 fingerprint 策略即可根治。
  • Submit 卡在 "Processing":ASC 端处理通常 30 分钟内完成,超过 2 小时一般是 IPA 里包含了非法 entitlement(比如 aps-environment 未配置但代码用了 Push)。

提速方面,除了前面提到的缓存与资源类,我推荐启用 EAS prebuild 缓存autoIncrement: "buildNumber" 配合 Git tag,把版本管理彻底自动化。最终目标是把「提交 PR → 合并 main → 用户拿到测试包」压到 20 分钟以内。如果你的 CI 还在用 Fastlane + 自建 Mac mini,下一次升级前认真评估一下迁移到 EAS 的投入产出比,体验差距是数量级的。

常见问题

EAS Build 与 EAS Update 的区别是什么?

EAS Build 产出可安装的二进制(IPA/AAB),原生代码或依赖变更必须经它发版;EAS Update 走 OTA 通道,仅更新 JS bundle 与资源,绕开商店审核但不能改原生层。两者搭配使用:Build 处理重大版本,Update 处理紧急 bug。

EAS Build 是免费的吗?

免费档每月 30 次构建,适合个人项目或小团队验证。Production 与 Enterprise 计划提供优先队列、M2 Pro 资源、更大并发与缓存配额,具体价格见 Expo 官方定价页。

没有 Mac 可以构建 iOS 应用吗?

可以。EAS Build 在云端 Mac 上构建,本地只需要任意操作系统都能跑 EAS CLI。凭证由 EAS 托管,无需 Xcode 或 Keychain,这是 EAS 相对传统 Fastlane 流程最大的优势之一。

一次完整的 EAS Build 大约需要多长时间?

免费档下,Android production 构建约 8 到 12 分钟,iOS 约 12 到 18 分钟,加上队列等待可能再增加 5 到 20 分钟。升级到 M2 Pro 资源类后构建时间约减半,且队列优先级最高。

EAS Update 推送会被苹果拒审吗?

Apple Guideline 4.7 允许通过 OTA 更新 JavaScript 与资源,前提是不引入未经审核的新功能或破坏性 UI 变更。常规 bug 修复、文案修改、样式调整完全合规,但新增完整功能模块仍应走 EAS Build 与商店审核。

Jake Morrison
关于作者 Jake Morrison

React Native lead engineer who's shipped six apps and learned six different lessons. Bullish on the New Architecture.