# React Native Expo 热更新项目
> 基于 Expo Router 和 EAS Update 的完整 React Native 项目,支持热更新(OTA)功能
[](https://expo.dev/)
[](https://reactnative.dev/)
[](https://www.typescriptlang.org/)
[](https://pnpm.io/)
## 📖 目录
- [项目特性](#-项目特性)
- [快速开始](#-快速开始)
- [项目结构](#-项目结构)
- [热更新使用指南](#-热更新使用指南)
- [开发指南](#-开发指南)
- [配置说明](#-配置说明)
- [常见问题](#-常见问题)
- [相关资源](#-相关资源)
## ✨ 项目特性
- 🎯 **Expo Router** - 文件路由系统,类似 Next.js,支持类型安全的导航
- 📘 **TypeScript** - 完整的类型支持,提升代码质量
- 🔥 **EAS Update** - 热更新支持(CodePush 的官方替代方案)
- ⚡ **React Native 新架构** - 启用 Fabric 渲染器和 TurboModules
- 📦 **pnpm** - 快速、节省磁盘空间的包管理器
- 🧭 **标签导航** - 开箱即用的导航示例
- 🎨 **主题支持** - 内置深色/浅色主题切换
- 📱 **跨平台** - 支持 iOS、Android 和 Web
## 🚀 快速开始
### 前置要求
- **Node.js** 18+ ([下载](https://nodejs.org/))
- **pnpm** ([安装指南](https://pnpm.io/installation))
- **Expo Go** App(可选,用于快速预览)
- [iOS App Store](https://apps.apple.com/app/expo-go/id982107779)
- [Android Play Store](https://play.google.com/store/apps/details?id=host.exp.exponent)
### 第一步:安装依赖
```bash
pnpm install
```
### 第二步:启动开发服务器
```bash
pnpm start
```
启动后,你会看到一个二维码和几个选项:
- 按 **`a`** - 在 Android 模拟器/设备上运行
- 按 **`i`** - 在 iOS 模拟器上运行(仅 macOS)
- 按 **`w`** - 在浏览器中运行
- **扫描二维码** - 在 Expo Go App 中运行
### 第三步:在设备上查看
#### 方法 1:使用 Expo Go(推荐用于快速预览)
1. 在手机上安装 Expo Go
2. 扫描终端中显示的二维码
3. 应用会自动加载并运行
#### 方法 2:使用模拟器
**Android 模拟器:**
```bash
pnpm android
```
**iOS 模拟器(仅 macOS):**
```bash
pnpm ios
```
**Web 浏览器:**
```bash
pnpm web
```
## 📁 项目结构
```
rn-demo/
├── 📱 app/ # Expo Router 路由文件
│ ├── (tabs)/ # 标签导航组
│ │ ├── index.tsx # 首页 - 热更新演示 ⭐
│ │ ├── two.tsx # 第二个标签页
│ │ ├── demo.tsx # 完整示例页面
│ │ └── _layout.tsx # 标签布局
│ ├── _layout.tsx # 根布局 - 自动检查更新 ⭐
│ ├── modal.tsx # 模态页面示例
│ ├── +html.tsx # Web HTML 模板
│ └── +not-found.tsx # 404 页面
│
├── 💻 src/ # 源代码目录
│ ├── utils/ # 工具函数
│ │ ├── api.ts # Axios API 配置
│ │ ├── storage.ts # AsyncStorage 封装
│ │ └── date.ts # Day.js 日期工具
│ ├── stores/ # Zustand 状态管理
│ │ ├── userStore.ts # 用户状态
│ │ └── settingsStore.ts # 应用设置
│ ├── schemas/ # Zod 验证规则
│ │ ├── auth.ts # 认证验证
│ │ └── user.ts # 用户验证
│ ├── services/ # API 服务层
│ │ ├── authService.ts # 认证服务
│ │ └── userService.ts # 用户服务
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useDebounce.ts # 防抖 Hook
│ │ ├── useThrottle.ts # 节流 Hook
│ │ └── useHaptics.ts # 触觉反馈 Hook
│ ├── types/ # TypeScript 类型
│ │ └── index.ts # 全局类型定义
│ └── index.ts # 统一导出 ⭐
│
├── 🧩 components/ # 可复用组件
│ ├── Themed.tsx # 主题化组件
│ ├── ExternalLink.tsx # 外部链接组件
│ └── useColorScheme.ts # 主题 Hook
│
├── 🎯 constants/ # 常量配置
│ └── Colors.ts # 颜色主题
│
├── 🎨 assets/ # 静态资源
│ ├── images/ # 图片资源
│ └── fonts/ # 字体文件
│
├── 📚 docs/ # 项目文档
│ ├── USAGE_EXAMPLES.md # 使用示例
│ └── LIBRARIES.md # 工具库使用指南
│
├── ⚙️ 配置文件
│ ├── .env.example # 环境变量示例 🎯 NEW!
│ ├── app.json # Expo 配置 ⭐
│ ├── eas.json # EAS 构建和更新配置 ⭐
│ ├── package.json # 项目依赖
│ ├── pnpm-lock.yaml # pnpm 锁文件
│ ├── tsconfig.json # TypeScript 配置
│ └── .gitignore # Git 忽略文件
│
└── 📄 文档文件
├── README.md # 项目主文档(本文件)
└── CHANGELOG.md # 更新日志
⭐ = 热更新相关的关键文件
🎯 = 新增的文件/目录
```
### 关键目录说明
#### 📱 `app/` - 路由和页面
- **`app/_layout.tsx`** - 应用启动时自动检查更新
- **`app/(tabs)/index.tsx`** - 热更新演示页面
- **`app/(tabs)/demo.tsx`** - 完整示例页面,展示所有工具的使用 🎯
#### 💻 `src/` - 源代码目录 🎯
项目的核心业务逻辑代码,包含:
- **`utils/`** - 工具函数(API、存储、日期)
- **`stores/`** - 状态管理(用户、设置)
- **`schemas/`** - 数据验证(认证、用户)
- **`services/`** - API 服务层
- **`hooks/`** - 自定义 Hooks
- **`types/`** - TypeScript 类型定义
- **`index.ts`** - 统一导出所有模块
#### 📚 `docs/` - 项目文档 🎯
完善的项目文档,包含:
- **使用指南** - 如何使用各个工具
- **代码示例** - 实际的代码示例
- **配置说明** - 项目配置详解
### 核心文件说明
#### 热更新相关 ⭐
- **`app.json`** - Expo 配置,包含热更新设置
- **`eas.json`** - EAS 构建和更新通道配置
- **`app/_layout.tsx`** - 自动检查更新逻辑
- **`app/(tabs)/index.tsx`** - 手动检查更新功能
#### 工具配置 🎯
- **`src/index.ts`** - 统一导出,从这里导入所有工具
- **`.env.example`** - 环境变量配置示例
- **`tsconfig.json`** - 配置了路径别名 `@/*`
### 已安装的工具库
项目已安装并配置好以下工具库:
| 类别 | 工具库 | 用途 |
| ------------ | ----------------------------------------- | ------------------- |
| **工具类** | lodash-es | JavaScript 工具函数 |
| | dayjs | 日期处理 |
| | axios | HTTP 请求 |
| **状态管理** | zustand | 轻量级状态管理 |
| **表单处理** | react-hook-form | 表单管理 |
| | zod | 数据验证 |
| **原生功能** | @react-native-async-storage/async-storage | 本地存储 |
| | expo-image | 优化的图片组件 |
| | expo-haptics | 触觉反馈 |
### 快速开始
1. **查看完整示例** - 运行应用,点击 "完整示例" tab 🎯
2. **阅读文档** - 查看 [docs/](./docs/) 目录中的文档
3. **使用工具** - 从 `@/src` 导入所需的工具
```typescript
// 示例:导入工具
import {
api,
Storage,
formatDate,
useUserStore,
authService,
useDebounce,
useHaptics,
} from '@/src';
```
## 🔥 热更新使用指南
> **重要提示**:热更新功能需要在真实构建(Development Build 或 Production Build)中测试,Expo Go 不支持自定义热更新配置。
### 步骤 1:登录 EAS
```bash
eas login
```
如果没有账号,访问 [expo.dev](https://expo.dev) 注册一个免费账号。
### 步骤 2:初始化 EAS 项目
```bash
eas init
```
这会:
- 创建一个唯一的项目 ID
- 自动更新 `app.json` 中的 `extra.eas.projectId`
### 步骤 3:构建开发版本
**Android 开发构建:**
```bash
eas build --profile development --platform android
```
**iOS 开发构建(需要 macOS 和 Apple 开发者账号):**
```bash
eas build --profile development --platform ios
```
构建过程需要 **10-20 分钟**。完成后:
1. 在 [expo.dev](https://expo.dev) 控制台下载构建的 APK/IPA 文件
2. 安装到你的设备上
### 步骤 4:发布热更新
1. **修改代码**(例如修改 `app/(tabs)/index.tsx` 中的文本)
2. **发布更新到指定通道:**
```bash
# 发布到开发通道
eas update --channel development --message "测试热更新功能"
# 发布到预览通道
eas update --channel preview --message "修复了某个 bug"
# 发布到生产通道
eas update --channel production --message "v1.0.1: 添加了新功能"
```
### 步骤 5:测试更新
1. 打开已安装的应用
2. 点击首页的 **"检查更新"** 按钮
3. 应用会自动下载更新
4. 点击 **"立即重启"** 应用更新
5. 重启后即可看到更新内容 🎉
### 更新通道说明
项目配置了三个更新通道(在 `eas.json` 中定义):
| 通道 | 用途 | 适用场景 |
| --------------- | -------- | -------------- |
| **development** | 开发环境 | 日常开发和测试 |
| **preview** | 预览环境 | 内部测试和 QA |
| **production** | 生产环境 | 正式发布给用户 |
不同的构建配置会订阅不同的更新通道:
```bash
# 构建订阅 development 通道的版本
eas build --profile development --platform android
# 构建订阅 preview 通道的版本
eas build --profile preview --platform android
# 构建订阅 production 通道的版本
eas build --profile production --platform android
```
### 推荐的发布流程
```bash
# 1. 开发和本地测试
pnpm start
# 2. 发布到 preview 通道进行内部测试
eas update --channel preview --message "测试新功能"
# 3. 测试通过后,发布到 production 通道
eas update --channel production --message "v1.0.1: 修复了登录问题"
# 4. 如果需要回滚
eas update --channel production --branch main --message "回滚到稳定版本"
```
### 监控和管理更新
```bash
# 查看所有更新历史
eas update:list
# 查看特定通道的更新
eas update:list --channel production
# 查看更新详情
eas update:view [update-id]
```
## 💻 开发指南
### 常用命令
```bash
# 开发相关
pnpm start # 启动开发服务器
pnpm android # 在 Android 上运行
pnpm ios # 在 iOS 上运行(仅 macOS)
pnpm web # 在浏览器中运行
# 热更新相关
eas login # 登录 EAS
eas init # 初始化 EAS 项目
eas build --profile development --platform android # 构建开发版本
eas update --channel development --message "更新说明" # 发布热更新
eas update:list # 查看更新历史
# 构建相关
eas build --profile preview --platform android # 构建预览版本
eas build --profile production --platform android # 构建生产版本
```
### 修改代码示例
尝试修改以下内容来测试热更新:
#### 示例 1:修改首页标题
打开 `app/(tabs)/index.tsx`,找到:
```tsx
🚀 热更新演示
```
改为:
```tsx
🎉 热更新测试成功!
```
#### 示例 2:修改按钮样式
在 `app/(tabs)/index.tsx` 的 `styles` 中修改:
```tsx
button: {
backgroundColor: '#FF6B6B', // 改为红色
paddingHorizontal: 30,
paddingVertical: 15,
borderRadius: 10,
marginBottom: 20,
minWidth: 200,
alignItems: 'center',
},
```
#### 示例 3:添加新功能
在首页添加一个计数器:
```tsx
const [count, setCount] = useState(0);
// 在 JSX 中添加
setCount(count + 1)}>
点击次数: {count}
;
```
修改后,运行 `eas update` 发布更新,然后在应用中检查更新即可看到变化。
### 自定义更新行为
#### 自动静默更新
修改 `app/_layout.tsx` 中的更新逻辑,移除 Alert 提示:
```tsx
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync(); // 直接重启,不提示
}
```
#### 强制更新
不提供"稍后"选项:
```tsx
Alert.alert(
'发现新版本',
'请更新到最新版本',
[
{
text: '立即更新',
onPress: async () => {
await Updates.reloadAsync();
},
},
],
{ cancelable: false } // 不可取消
);
```
## ⚙️ 配置说明
### app.json 配置
关键配置项:
```json
{
"expo": {
"name": "rn-demo",
"slug": "rn-demo",
"version": "1.0.0",
"newArchEnabled": true, // 启用 React Native 新架构
"updates": {
"url": "https://u.expo.dev/your-project-id" // EAS Update 服务器
},
"runtimeVersion": {
"policy": "appVersion" // 运行时版本策略
},
"plugins": [
"expo-router", // Expo Router 插件
"expo-updates" // 热更新插件
],
"ios": {
"bundleIdentifier": "com.rndemo.app"
},
"android": {
"package": "com.rndemo.app"
}
}
}
```
**配置说明:**
- `updates.url` - EAS Update 服务器地址(运行 `eas init` 后自动生成)
- `runtimeVersion.policy` - 运行时版本策略,确保更新兼容性
- `appVersion` - 基于 `version` 字段(当前使用)
- `nativeVersion` - 基于原生构建号
- `plugins` - 启用的 Expo 插件
- `newArchEnabled` - 启用 React Native 新架构(Fabric + TurboModules)
### eas.json 配置
构建和更新配置:
```json
{
"build": {
"development": {
"developmentClient": true, // 开发客户端
"distribution": "internal", // 内部分发
"channel": "development" // 订阅 development 更新通道
},
"preview": {
"distribution": "internal",
"channel": "preview" // 订阅 preview 更新通道
},
"production": {
"channel": "production" // 订阅 production 更新通道
}
}
}
```
**配置说明:**
- `developmentClient` - 是否为开发客户端(包含开发工具)
- `distribution` - 分发方式(`internal` 或 `store`)
- `channel` - 更新通道,决定应用接收哪个通道的更新
## 📋 热更新最佳实践
### 何时使用热更新
✅ **适合热更新的场景:**
- ✅ 修复 JavaScript/TypeScript 代码 bug
- ✅ 更新 UI 样式和布局
- ✅ 修改业务逻辑
- ✅ 更新文本内容和翻译
- ✅ 调整配置参数
- ✅ 添加新的 JS 功能
❌ **不适合热更新的场景(需要重新构建):**
- ❌ 添加/删除原生依赖(如 `react-native-camera`)
- ❌ 修改原生代码(iOS/Android)
- ❌ 更改应用权限(如相机、位置权限)
- ❌ 修改 `app.json` 中的原生配置
- ❌ 升级 React Native 或 Expo SDK 版本
- ❌ 修改应用图标或启动屏幕
### 版本管理策略
**appVersion 策略**(当前使用):
- 基于 `app.json` 中的 `version` 字段
- ✅ 优点:简单直观,易于理解
- ⚠️ 注意:每次原生构建需要手动更新版本号
**nativeVersion 策略**:
- 基于原生构建号(iOS 的 `buildNumber`,Android 的 `versionCode`)
- ✅ 优点:自动管理,无需手动更新
- ⚠️ 注意:需要配置原生构建号
### 更新策略建议
1. **开发阶段**:频繁发布到 `development` 通道
2. **测试阶段**:发布到 `preview` 通道,进行 QA 测试
3. **生产发布**:测试通过后发布到 `production` 通道
4. **紧急修复**:直接发布到 `production` 通道,快速修复线上问题
## ❓ 常见问题
### Q: 为什么在 Expo Go 中看不到热更新功能?
**A:** Expo Go 是开发工具,不支持自定义热更新。需要使用 `eas build` 构建真实应用。
### Q: 更新后没有生效?
**A:** 检查以下几点:
1. 确保应用完全关闭后重新打开(不是后台切换)
2. 检查更新通道是否匹配(development/preview/production)
3. 确保 `runtimeVersion` 匹配
4. 查看控制台是否有错误信息
5. 检查网络连接
### Q: 如何回滚到之前的版本?
**A:**
```bash
# 查看更新历史
eas update:list --channel production
# 重新发布之前的更新
eas update --channel production --branch main --message "回滚到稳定版本"
```
### Q: 热更新的大小限制是多少?
**A:** EAS Update 没有严格的大小限制,但建议:
- 保持更新包 < 10MB 以确保良好的用户体验
- 避免在更新中包含大量图片或资源文件
- 使用 CDN 托管大型资源
### Q: 如何强制用户更新?
**A:** 修改 `app/_layout.tsx` 中的更新逻辑:
```tsx
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
Alert.alert(
'发现新版本',
'请更新到最新版本',
[{ text: '立即更新', onPress: async () => await Updates.reloadAsync() }],
{ cancelable: false } // 不可取消
);
}
```
### Q: 更新检查的频率是多少?
**A:**
- **应用启动时**:自动检查(在 `app/_layout.tsx` 中配置)
- **手动检查**:用户点击"检查更新"按钮
- **后台检查**:可以配置定时检查(需要自己实现)
### Q: 如何在开发时测试热更新?
**A:**
1. 构建 Development Build:`eas build --profile development --platform android`
2. 安装到设备
3. 修改代码
4. 发布更新:`eas update --channel development --message "测试"`
5. 在应用中点击"检查更新"
### Q: Web 平台的警告信息怎么处理?
**A:** 运行 `pnpm web` 时可能会看到 `pointerEvents` 的弃用警告,这是 `react-native-web` 库的内部问题,不影响功能,可以安全忽略。详见 [docs/KNOWN_ISSUES.md](./docs/KNOWN_ISSUES.md)。
## 💡 开发提示
- 开发时使用 `pnpm start` 即可,支持快速刷新(Fast Refresh)
- 只有在测试热更新功能时才需要构建真实应用
- 热更新只能更新 JavaScript/TypeScript 代码,不能更新原生代码
- 建议使用 Git 管理代码版本,方便回滚
- 每次发布更新时添加清晰的 `--message` 说明,便于追踪
## 📚 相关资源
### 官方文档
- [Expo 官方文档](https://docs.expo.dev/)
- [Expo Router 文档](https://docs.expo.dev/router/introduction/)
- [EAS Update 文档](https://docs.expo.dev/eas-update/introduction/)
- [EAS Build 文档](https://docs.expo.dev/build/introduction/)
- [React Native 文档](https://reactnative.dev/)
### 学习资源
- [Expo Router 最佳实践](https://docs.expo.dev/router/best-practices/)
- [EAS Update 最佳实践](https://docs.expo.dev/eas-update/best-practices/)
- [React Native 新架构](https://reactnative.dev/docs/the-new-architecture/landing-page)
### 社区
- [Expo Discord](https://chat.expo.dev/)
- [Expo Forums](https://forums.expo.dev/)
- [React Native Community](https://reactnative.dev/community/overview)
## 📚 项目文档
更多详细文档请查看 [docs](./docs/) 目录:
- **[使用示例](./docs/USAGE_EXAMPLES.md)** - 实际代码示例
- **[工具库使用指南](./docs/LIBRARIES.md)** - 详细的工具库使用方法和示例
---
**祝你开发愉快!** 🎉
如有问题,请查看 [常见问题](#-常见问题) 或访问 [Expo 官方文档](https://docs.expo.dev/)。