CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React Native 开发入门/课时/测试与发布
RawBlame
课时目录12 节

React Native 开发入门

  1. 01RN 与 Expo
  2. 02核心组件
  3. 03布局与列表
  4. 04平台差异
  5. 05导航与手势
  6. 06State 与 JSX
  7. 07网络
  8. 08本地存储
  9. 09原生模块
  10. 10动画
  11. 11性能优化
  12. 12测试与发布

测试与发布

Jest/Detox 与商店。

30 分钟

测试与发布

这一课你将能做到

  1. 用 jest-expo 和 @testing-library/react-native 给工具函数与组件写基础测试
  2. 用 EAS Build 在云端打出 iOS / Android 安装包,走通 TestFlight 内测与商店上架
  3. 理解 OTA 热更新的边界与 runtimeVersion 管理,安全地推送线上修复

为什么要学

代码写完到用户手里,中间隔着「测试 → 构建 → 上架 → 更新」四道关。原生时代每道关都要配 Xcode、证书、签名、开发者账号,劝退无数前端工程师。Expo 的 EAS(Expo Application Services)把它们全部云化——从代码到安装包一键出,没有 Mac 也能上架 iOS。这是 Expo 对初学者最大的善意。

测试同样如此。很多人写 App 靠「自己点一遍」,但 20 个功能 × 5 种状态 × 2 个平台 = 200 种组合,手动点不过来。Jest 单元测试加 Testing Library 的组件交互测试,把回归成本降到零。

人话:EAS Build 就像云打印店——你把源码(电子稿)发给它,它在云端用 Mac 机器帮你出 iOS 安装包(你本地连 Mac 都没有也行),印好了给你取件码(下载链接)。而 OTA 热更新则是「云打印店还能帮你换页眉」——只改 JS 文案或逻辑,不用重新出书(商店审核),用户下次打开自动生效。

先建立图像

从代码到用户的完整发布路线图:

本地开发完成
  ↓
Jest 测试通过(工具函数 + 核心组件交互)
  ↓
eas build --profile preview(出可内测包)
  → Android:直接给同事装 APK / 二维码
  → iOS:TestFlight 邀请内测邮箱
    ↓
内测没问题
  ↓
eas build --profile production(出商店包)
  → 手动上传到 App Store Connect / Google Play Console
  → 或 eas submit 自动提交
    ↓
审核通过,用户下载
    ↓
日常修 bug / 改文案:eas update --branch production(OTA,免审核)

跟着做

步骤 1:Jest 测试环境

Expo 项目自带 jest-expo 预设,装一下就能跑:

npx expo install jest-expo jest --dev

package.json 加脚本和预设:

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  },
  "jest": {
    "preset": "jest-expo"
  }
}

然后 npm test 就能跑。jest-expo 预设帮你处理好了 RN 组件的 mock(AsyncStorage、相机模块等),不需要额外配置。

步骤 2:写第一个工具函数测试

// utils/format.js
export const formatPrice = (cents) => {
  if (typeof cents !== 'number' || cents < 0) return '¥0.00';
  return `¥${(cents / 100).toFixed(2)}`;
};

export const truncate = (str, len = 20) => {
  if (!str || str.length <= len) return str;
  return str.slice(0, len) + '...';
};
// utils/__tests__/format.test.js
import { formatPrice, truncate } from '../format';

describe('formatPrice', () => {
  test('正常价格', () => {
    expect(formatPrice(999)).toBe('¥9.99');
    expect(formatPrice(0)).toBe('¥0.00');
    expect(formatPrice(123456)).toBe('¥1234.56');
  });

  test('边界值', () => {
    expect(formatPrice(-1)).toBe('¥0.00');
    expect(formatPrice(null)).toBe('¥0.00');
    expect(formatPrice('abc')).toBe('¥0.00');
  });
});

describe('truncate', () => {
  test('短字符串不截断', () => {
    expect(truncate('你好')).toBe('你好');
  });

  test('长字符串截断加省略号', () => {
    expect(truncate('这是一段非常长的文字内容', 8)).toBe('这是一段非常长的...');
  });
});

运行 npm test,绿了继续。工具函数测试是投入产出比最高的:输入固定、输出确定、没有 UI 依赖、跑得快、覆盖业务核心逻辑。

步骤 3:组件交互测试

给 @testing-library/react-native 装依赖(jest-expo 预设已兼容):

npx expo install @testing-library/react-native --dev
// components/Counter.js
import { useState } from 'react';
import { Button, Text, View } from 'react-native';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <View>
      <Text testID="count">{count}</Text>
      <Button title="点我 +1" onPress={() => setCount(c => c + 1)} />
    </View>
  );
}
// components/__tests__/Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react-native';
import Counter from '../Counter';

test('点按钮计数加一', () => {
  render(<Counter />);

  // 以用户视角查元素:findByText 查找可见文字
  const btn = screen.getByText('点我 +1');
  fireEvent.press(btn);

  // 验证结果:计数文本变成 1
  expect(screen.getByText('1')).toBeTruthy();
});

test('初始状态是 0', () => {
  render(<Counter />);
  expect(screen.getByTestId('count').children[0]).toBe('0');
});

Testing Library 的核心哲学:以用户视角测试组件。用户看不到组件内部 state,用户看到的是文字和按钮。所以测试里不写 expect(wrapper.state()),而是写 expect(screen.getByText(...))——测试越贴近真实用户操作,越有保护力。

步骤 4:EAS 云端构建

EAS CLI 安装与登录:

npm install -g eas-cli
eas login                    # 用 Expo 账号登录(免费注册)

初始化项目:

eas build:configure          # 自动检测项目,生成 eas.json

eas.json 是 EAS 的配置中心,三种 profile 对应三种场景:

{
  "cli": { "version": ">= 7.0.0" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "android": { "buildType": "apk" }
    },
    "production": {
      "autoIncrement": true
    }
  }
}

三种 profile 的用途:

  • development:带调试器的开发包(可连 Flipper / React DevTools),日常开发用,需要 expo-dev-client
  • preview:给同事/朋友装的内测包,出 APK(安卓直接装)或 ad-hoc IPA(iOS 指定设备安装)
  • production:提交商店审核的包,开启代码优化、资源压缩

出包命令:

# Android 内测包(出 APK,发群里就能装)
eas build --platform android --profile preview

# iOS 内测包(出 IPA,需 Apple 开发者账号 $99/年)
eas build --platform ios --profile preview

# 生产包(上架用)
eas build --platform all --profile production

构建在 Expo 的服务器上进行,本地不需要 Mac、不需要 Android Studio,完成后给你一个下载链接或二维码。构建日志可以在网页上实时查看。

步骤 5:内测分发

Android:最简单。APK 链接发给同事直接安装,或者上传到自己的 CDN / 分发平台(如蒲公英)。

iOS:TestFlight 是标准流程:

# 把 preview 包提交到 App Store Connect
eas submit --platform ios

然后在 App Store Connect 后台:

  1. 进入「我的 App」→「TestFlight」
  2. 添加内部测试员(用你的 Apple ID 就行,最多 100 人)
  3. 测试员会收到邮件,安装 TestFlight App,下载内测版本

步骤 6:正式上架

iOS(App Store):

  • App Store Connect 填资料:应用名、副标题、截图(6.5 寸 iPhone + 12.9 寸 iPad)、描述、隐私问卷
  • 隐私合规是近年审核重点:收集了什么数据、用到了什么权限,必须如实声明
  • 提交审核,通常 1-3 天出结果。被拒原因 80% 是:权限用途文案不清、测试账号没提供、隐私声明不完整

Android(Google Play):

  • Google Play Console 填类似资料
  • 国内上架还要:① 各手机厂商应用商店(华为、小米、OPPO、vivo…);② 软件著作权(国内应用商店硬性要求,提前申请);③ 隐私合规自评估报告

隐私合规提示:App 里用到的每一个权限、收集的每一个字段,都必须在上架资料里如实声明。别抱侥幸心理——审核系统越来越智能,虚假声明会被下架甚至封号。

步骤 7:OTA 热更新(日常修 bug 的神器)

改了 JS 代码或文案,不想走商店审核?OTA 热更新让你分钟级推送修复:

# 推送一条更新到 production 分支
eas update --branch production --message "修复首页加载慢的问题"

用户下次打开 App 时,会自动拉取新的 JS bundle 并重启生效。整个过程用户无感知(可以在后台静默完成)。

OTA 的硬性边界(这条边界决定了你的开发策略):

能走 OTA必须重新 build 上架
JS 代码改动新增原生模块(如装了 expo-camera)
样式调整权限变更(新增权限声明)
文案/逻辑修改app.json 原生配置变更
图片/静态资源替换修改原生代码(Swift/Kotlin)

也就是说:你加了个按钮、改了 API 接口地址、调了文案——OTA 搞定。你新装了 expo-location 要调定位——必须重新 build 上架。

runtimeVersion 管理:OTA 只能推给「原生层匹配」的 App。如果新版本改了大量原生依赖,runtimeVersion 变了,旧版本 App 不会收到这条更新(否则原生层不支持新 JS 代码会白屏崩溃)。

{
  "expo": {
    "runtimeVersion": { "policy": "appVersion" }  // 按 app.json 的 version 自动管理
  }
}

推荐用 appVersion 策略自动管理,别手动写字符串——手动维护版本号极容易推错。

讲清楚要点

  1. 测试金字塔在 RN 同样成立:大量工具函数单测(快、稳、覆盖核心逻辑) + 少量关键组件交互测试(模拟用户操作) + 极少量 E2E(真机跑流程)。别追求 UI 全覆盖,投入产出比低,维护成本高。
  2. EAS Build 的三种 profile 分工明确:development 连调试器、preview 给同事装、production 上架。日常开发用 development,发给别人用 preview,只有上架才出 production。
  3. iOS 上架绕不开 Apple Developer Program:$99/年(个人或公司账号),TestFlight 内测和 App Store 上架都需要。这是苹果收的入场券,省不了。好消息是 EAS 让你在云端完成全部构建和签名,本地不需要 Mac。
  4. OTA 是 RN 的超级武器:JS 层改动分钟级全量推送,零审核等待。但加了新的原生依赖(比如新装了 expo-camera)必须重新 build 上架。开发策略上:尽量把变更控制在 JS 层,最大化利用 OTA 提速。
  5. 隐私合规不是可选项:无论 iOS 还是 Android,权限和数据收集声明都是审核红线。App 里用什么写什么,措辞要让审核员和用户都看得懂。
  6. 没有 Mac 也能上架 iOS:EAS Build + eas submit 全在云端完成,证书也可以用 eas credentials 托管。这是 Expo 相对纯 RN 开发流程的最大优势之一。

你可能会懵的地方

  • 没有 Mac 怎么搞 iOS 上架? 完全不需要 Mac。EAS Build 在云端 Mac 上构建,eas submit 自动提交到 App Store Connect,证书用 eas credentials 命令让 EAS 云端托管生成。你唯一需要本地操作的是:在 Apple Developer 网站申请开发者账号、在 App Store Connect 填上架资料。构建和签名全部云端化。

  • OTA 热更新会被苹果拒吗? 不会——苹果明确允许「不实质性改变 App 用途」的 JS 热更新。EAS Update 和之前的 CodePush 都被数百万 App 使用。但红线是:不能用 OTA 把 App 的核心功能完全替换(比如把计算器改成赌博 App),这种会被下架。

  • EAS 收费吗? 有免费额度(每月有限构建次数和 OTA 推送量),个人学习和小团队内测够用。高频构建或大团队需要订阅 EAS 付费计划。如果只是本地开发,完全免费。

  • 测试要写到什么程度? 工具函数、数据转换、关键业务逻辑(如价格计算、权限判断)必须有测试;UI 组件挑核心交互测(点按钮、表单提交、列表加载);网络请求一律 mock,不要调真实接口。覆盖率不是目标,「关键路径不出回归 bug」才是。

  • 国内上架有什么额外要求? ① 软件著作权(软著),国内应用商店硬性要求;② 各厂商商店(华为、小米、OPPO、vivo)需要分别申请开发者账号并单独提交;③ 隐私合规自评估报告;④ ICP 备案(含网络服务的 App)。其中软著办理周期 1-2 个月,建议提前准备。

  • Android 的 APK 和 AAB 怎么选? preview 默认出 APK,可以直接安装;Google Play 上架要求上传 AAB(Android App Bundle),EAS production 构建默认出 AAB。国内应用商店大多也接受 AAB,少数(如小米早期版本)可能仍要 APK,按商店要求调整 eas.json 的 buildType 即可。

错法对照

错法问题正解
本地改了原生依赖还发 OTA用户端打开白屏或崩溃原生变更必须重新 build 上架
只在模拟器测试就上架真机性能、权限、手势问题全漏至少一台 iOS + 一台 Android 真机完整过一遍
隐私权限乱声明 / 不声明审核被拒用了什么写什么,文案清晰、用户视角
runtimeVersion 手动写死OTA 推送到不匹配的原生包,白屏用 policy: "appVersion" 自动管理
不发 preview 直接 production内测问题全带给最终用户先 preview → TestFlight → 上架
测试里直接请求真实接口测试慢、不稳定、依赖网络mock fetch / axios
上架资料截图用模拟器截苹果审核要求用真机截图用真机截,或直接按尺寸规范合成
生产包 console.log 没删JS 线程被同步日志拖慢生产环境去掉 console.log,或用 babel 插件自动移除

卡点急救

  1. eas build 排队太久:免费账户高峰期排队(尤其 iOS build 用 Mac 资源更紧张),错峰构建或升级 EAS 付费计划。Android 本地构建可以 eas build --platform android --local 在本地出包。
  2. TestFlight 构建版本不显示:检查 version / build 号是否比上一次递增;刚上传的构建需要苹果服务器处理几分钟;TestFlight 里要切换到正确的 App 版本。
  3. OTA 更新后用户没收到:确认分支(branch)和 runtimeVersion 匹配——旧 App 只拉旧 runtimeVersion 的更新;更新在下次冷启动时生效(用户杀掉 App 再打开),不是实时的。
  4. App Store 审核被拒:先看拒审邮件里的具体原因,通常集中在:① 权限用途文案不清晰;② 没有提供测试账号;③ 隐私声明不完整;④ 功能描述和实际体验不符。针对性修复后回复审核团队即可。
  5. Android 安装包太大:检查是否误打包了原生库的多架构版本(arm64 + armeabi + x86);EAS production 构建默认只出 arm64,不需要手动处理。

面试常问

  • EAS Build 相比本地打包的优势? ① 云端构建不需要本地 Mac / Android Studio 环境;② 证书统一托管,团队共享构建产物;③ CI/CD 集成简单(GitHub Actions 调用 eas build 一行搞定);④ 跨平台统一配置(一个 eas.json 管两端)。劣势是对第三方服务的依赖,极敏感场景可能选择本地自主构建。

  • OTA 热更新的原理和边界? App 启动时检查 Expo 更新服务器,下载新的 JS bundle 替换本地缓存,然后重启生效。边界:只能更新 JS 代码和静态资源;原生代码变更(新装原生模块、权限配置变更、AndroidManifest / Info.plist 修改)必须走商店发版。runtimeVersion 保证旧版 App 不会收到不兼容的新 bundle。

  • RN 项目怎么做测试? 单元测试用 Jest(jest-expo 预设已配好 RN 环境);组件交互测试用 React Native Testing Library(以用户视角查元素、触发事件、断言可见结果);E2E 用 Maestro 或 Detox 跑真机流程。网络层一律 mock,不要依赖真实接口。

自测三问

  1. EAS 的 development / preview / production 三种 profile 各自用途是什么?
  2. 什么改动可以走 OTA 热更新?什么改动必须重新 build 上架?
  3. 没有 Mac 的情况下,如何完成 iOS 构建和 App Store 上架?

小结一句话

从代码到用户:Jest 保质量、EAS 管构建、TestFlight 做内测、商店审核上架、OTA 做日常更新——Expo 把发布这条最难的路铺平了。

延伸

  • EAS 官方文档
  • React Native Testing Library 文档
  • Jest 官方文档

至此 React Native Essentials 系列完结。如果这 12 课的内容你已经跟着跑通了代码,强烈建议挑一个真实小项目(记账 App、天气查询、待办清单)完整走一遍「开发 → 测试 → 构建 → 上架 → OTA 更新」的全流程。真实项目是最好的老师,只有亲手把 App 装进用户手机,这些知识才会变成肌肉记忆。

加载本节练习…
← 上一课性能优化