Chapter 09

EAS:从源码到商店、到玩家手机

EAS 是 Expo 官方的 Build/Submit/Update 三件套,替你免装 Xcode、Android Studio 云打包,走 CI/CD 上商店,再用 OTA 推补丁。对小团队意味着「把 App 这事儿外包给 Expo」。

安装 eas-cli

npm install -g eas-cli
eas login
eas init                # 关联 Expo 项目 ID

eas.json 配置

{
  "cli": { "version": ">= 13.0.0" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "channel": "development"
    },
    "preview": {
      "distribution": "internal",
      "ios": { "simulator": true },
      "channel": "preview"
    },
    "production": {
      "autoIncrement": true,
      "channel": "production"
    }
  },
  "submit": {
    "production": {
      "ios": {
        "appleId": "dev@myapp.com",
        "ascAppId": "1234567890",
        "appleTeamId": "AB12CD34EF"
      },
      "android": {
        "serviceAccountKeyPath": "./google-service-key.json",
        "track": "production"
      }
    }
  }
}

三个 profile:

development
带 dev-client 的自定义包,可以连本地 Metro,能装任意 native 包。平时开发装这个到真机。
preview
release 模式但内部分发,给团队/客户试用。iOS 可生成模拟器版本。
production
上架商店的包,autoIncrement 自动递增构建号。

云端打包

# 开发包
eas build --profile development --platform ios
eas build --profile development --platform android

# 生产
eas build --profile production --platform all

EAS 在云端 macOS/Linux 跑 expo prebuild + xcodebuild / gradle,产物 .ipa/.aab 链接给你下载。没 Xcode 也能出 iOS 包(但生产需要 Apple 开发者账号提供证书 — EAS 帮你托管)。

本地打包(对照)

# 生成 ios/android 目录
npx expo prebuild

# iOS 开 Xcode,Android 开 Android Studio 打包
cd ios && pod install && xcodebuild ...
cd android && ./gradlew bundleRelease

EAS 把这套流水线搬云端而已。你可以手工,也可以让它跑。

提交商店

eas submit --profile production --platform all
# iOS 推到 TestFlight,Android 推 Play Console 的指定 track

OTA:expo-updates

OTA(Over-The-Air)= 不发新版,直接把新 JS bundle 推到用户端。适合改文案、修 bug,只要没动 native 代码。

npx expo install expo-updates
eas update:configure    # 自动改 app.json 加 updates 字段
{
  "expo": {
    "updates": {
      "url": "https://u.expo.dev/your-project-id",
      "enabled": true,
      "checkAutomatically": "ON_LOAD",
      "fallbackToCacheTimeout": 0
    },
    "runtimeVersion": { "policy": "appVersion" }
  }
}

发推送

# 发布到 production channel(对应 production profile 的包)
eas update --branch production --message "修复登录 bug"

# 发 preview
eas update --branch preview --message "内测新功能"

几秒钟后,你的用户下次冷启动 App 会检查新 bundle,下载后下次启动生效。

Channel 与 Branch

Channel
构建产物带的标签,写在 eas.json 的 build.channel。一经打包不可改。
Branch
Update 的分支,可以动态切换 channel 指向哪个 branch。生产事故时,把 production channel 指回旧 branch = 一键回滚。
# 回滚:把 production channel 指回 v1.2 branch
eas channel:edit production --branch v1.2

# 查看当前 channel 关联
eas channel:view production

runtimeVersion 与原生代码

OTA 只能推 JS,不能改 native
如果你加了新的 native 模块或升 Expo SDK,runtimeVersion 必须改,旧版本 App 拿不到这次 update——他们得从商店升包。推荐 {"policy": "appVersion"},用 app.json 里 version 字段自动驱动。

生产时代码里兜一下

import * as Updates from 'expo-updates';

async function checkForUpdate() {
  try {
    const { isAvailable } = await Updates.checkForUpdateAsync();
    if (isAvailable) {
      await Updates.fetchUpdateAsync();
      Alert.alert('有新版本', '重启应用?', [
        { text: '稍后' },
        { text: '立刻重启', onPress: () => Updates.reloadAsync() },
      ]);
    }
  } catch (e) { console.warn(e); }
}

EAS Workflows:CI/CD 全托管

.eas/workflows/
└── publish.yml
name: Publish
on:
  push:
    branches: [main]
jobs:
  ota:
    steps:
      - uses: eas/checkout
      - uses: eas/install_node_modules
      - uses: eas/update
        with:
          branch: production
          message: "auto from ${{ github.sha }}"

或直接用 GitHub Actions 调 EAS CLI:

- name: OTA update
  run: eas update --branch production --non-interactive
  env:
    EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}

常见坑

iOS 证书
第一次 eas build 会问要不要让 EAS 管证书——选 yes 最省事。想自己管则 eas credentials 手动导入 .p12/Provisioning Profile。
Android 签名
EAS 默认生成 keystore 并托管。导出到 Play Console 的 key upload 只有一次机会,存好 eas credentials 里的备份。
OTA 老用户不更新
App 启动时会去 u.expo.dev 查 update,网络差时跳过用本地缓存。可以手动 checkForUpdateAsync 触发。
苹果拒绝 OTA
苹果审核规则:OTA 只能 JS/资产,不能变业务性质(假比:做的是记账 App,OTA 推成游戏,拒)。保守起见,重大改动仍走商店审核。

成本

自托管方案:@expo/eas-build-action 自建 Runner,或 expo-updates 的协议是公开的,用 S3 自己实现 update server。中型以上团队会走自建。

本章小结