20 KiB
React Native Expo 热更新项目
基于 Expo Router 和 EAS Update 的完整 React Native 项目,支持热更新(OTA)功能
📖 目录
✨ 项目特性
- 🎯 Expo Router - 文件路由系统,类似 Next.js,支持类型安全的导航
- 📘 TypeScript - 完整的类型支持,提升代码质量
- 🔥 EAS Update - 热更新支持(CodePush 的官方替代方案)
- ⚡ React Native 新架构 - 启用 Fabric 渲染器和 TurboModules
- 📦 pnpm - 快速、节省磁盘空间的包管理器
- 🧭 标签导航 - 开箱即用的导航示例
- 🎨 主题支持 - 内置深色/浅色主题切换
- 📱 跨平台 - 支持 iOS、Android 和 Web
🚀 快速开始
前置要求
第一步:安装依赖
pnpm install
第二步:启动开发服务器
pnpm start
启动后,你会看到一个二维码和几个选项:
- 按
a- 在 Android 模拟器/设备上运行 - 按
i- 在 iOS 模拟器上运行(仅 macOS) - 按
w- 在浏览器中运行 - 扫描二维码 - 在 Expo Go App 中运行
第三步:在设备上查看
方法 1:使用 Expo Go(推荐用于快速预览)
- 在手机上安装 Expo Go
- 扫描终端中显示的二维码
- 应用会自动加载并运行
方法 2:使用模拟器
Android 模拟器:
pnpm android
iOS 模拟器(仅 macOS):
pnpm ios
Web 浏览器:
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/- 自定义 Hookstypes/- 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 | 触觉反馈 |
快速开始
- 查看完整示例 - 运行应用,点击 "完整示例" tab 🎯
- 阅读文档 - 查看 docs/ 目录中的文档
- 使用工具 - 从
@/src导入所需的工具
// 示例:导入工具
import {
api,
Storage,
formatDate,
useUserStore,
authService,
useDebounce,
useHaptics,
} from '@/src';
🔥 热更新使用指南
重要提示:热更新功能需要在真实构建(Development Build 或 Production Build)中测试,Expo Go 不支持自定义热更新配置。
步骤 1:登录 EAS
eas login
如果没有账号,访问 expo.dev 注册一个免费账号。
步骤 2:初始化 EAS 项目
eas init
这会:
- 创建一个唯一的项目 ID
- 自动更新
app.json中的extra.eas.projectId
步骤 3:构建开发版本
Android 开发构建:
eas build --profile development --platform android
iOS 开发构建(需要 macOS 和 Apple 开发者账号):
eas build --profile development --platform ios
构建过程需要 10-20 分钟。完成后:
- 在 expo.dev 控制台下载构建的 APK/IPA 文件
- 安装到你的设备上
步骤 4:发布热更新
-
修改代码(例如修改
app/(tabs)/index.tsx中的文本) -
发布更新到指定通道:
# 发布到开发通道
eas update --channel development --message "测试热更新功能"
# 发布到预览通道
eas update --channel preview --message "修复了某个 bug"
# 发布到生产通道
eas update --channel production --message "v1.0.1: 添加了新功能"
步骤 5:测试更新
- 打开已安装的应用
- 点击首页的 "检查更新" 按钮
- 应用会自动下载更新
- 点击 "立即重启" 应用更新
- 重启后即可看到更新内容 🎉
更新通道说明
项目配置了三个更新通道(在 eas.json 中定义):
| 通道 | 用途 | 适用场景 |
|---|---|---|
| development | 开发环境 | 日常开发和测试 |
| preview | 预览环境 | 内部测试和 QA |
| production | 生产环境 | 正式发布给用户 |
不同的构建配置会订阅不同的更新通道:
# 构建订阅 development 通道的版本
eas build --profile development --platform android
# 构建订阅 preview 通道的版本
eas build --profile preview --platform android
# 构建订阅 production 通道的版本
eas build --profile production --platform android
推荐的发布流程
# 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 "回滚到稳定版本"
监控和管理更新
# 查看所有更新历史
eas update:list
# 查看特定通道的更新
eas update:list --channel production
# 查看更新详情
eas update:view [update-id]
💻 开发指南
常用命令
# 开发相关
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,找到:
<Text style={styles.title}>🚀 热更新演示</Text>
改为:
<Text style={styles.title}>🎉 热更新测试成功!</Text>
示例 2:修改按钮样式
在 app/(tabs)/index.tsx 的 styles 中修改:
button: {
backgroundColor: '#FF6B6B', // 改为红色
paddingHorizontal: 30,
paddingVertical: 15,
borderRadius: 10,
marginBottom: 20,
minWidth: 200,
alignItems: 'center',
},
示例 3:添加新功能
在首页添加一个计数器:
const [count, setCount] = useState(0);
// 在 JSX 中添加
<TouchableOpacity style={styles.button} onPress={() => setCount(count + 1)}>
<Text style={styles.buttonText}>点击次数: {count}</Text>
</TouchableOpacity>;
修改后,运行 eas update 发布更新,然后在应用中检查更新即可看到变化。
自定义更新行为
自动静默更新
修改 app/_layout.tsx 中的更新逻辑,移除 Alert 提示:
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync(); // 直接重启,不提示
}
强制更新
不提供"稍后"选项:
Alert.alert(
'发现新版本',
'请更新到最新版本',
[
{
text: '立即更新',
onPress: async () => {
await Updates.reloadAsync();
},
},
],
{ cancelable: false } // 不可取消
);
⚙️ 配置说明
app.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 配置
构建和更新配置:
{
"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) - ✅ 优点:自动管理,无需手动更新
- ⚠️ 注意:需要配置原生构建号
更新策略建议
- 开发阶段:频繁发布到
development通道 - 测试阶段:发布到
preview通道,进行 QA 测试 - 生产发布:测试通过后发布到
production通道 - 紧急修复:直接发布到
production通道,快速修复线上问题
❓ 常见问题
Q: 为什么在 Expo Go 中看不到热更新功能?
A: Expo Go 是开发工具,不支持自定义热更新。需要使用 eas build 构建真实应用。
Q: 更新后没有生效?
A: 检查以下几点:
- 确保应用完全关闭后重新打开(不是后台切换)
- 检查更新通道是否匹配(development/preview/production)
- 确保
runtimeVersion匹配 - 查看控制台是否有错误信息
- 检查网络连接
Q: 如何回滚到之前的版本?
A:
# 查看更新历史
eas update:list --channel production
# 重新发布之前的更新
eas update --channel production --branch main --message "回滚到稳定版本"
Q: 热更新的大小限制是多少?
A: EAS Update 没有严格的大小限制,但建议:
- 保持更新包 < 10MB 以确保良好的用户体验
- 避免在更新中包含大量图片或资源文件
- 使用 CDN 托管大型资源
Q: 如何强制用户更新?
A: 修改 app/_layout.tsx 中的更新逻辑:
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
Alert.alert(
'发现新版本',
'请更新到最新版本',
[{ text: '立即更新', onPress: async () => await Updates.reloadAsync() }],
{ cancelable: false } // 不可取消
);
}
Q: 更新检查的频率是多少?
A:
- 应用启动时:自动检查(在
app/_layout.tsx中配置) - 手动检查:用户点击"检查更新"按钮
- 后台检查:可以配置定时检查(需要自己实现)
Q: 如何在开发时测试热更新?
A:
- 构建 Development Build:
eas build --profile development --platform android - 安装到设备
- 修改代码
- 发布更新:
eas update --channel development --message "测试" - 在应用中点击"检查更新"
Q: Web 平台的警告信息怎么处理?
A: 运行 pnpm web 时可能会看到 pointerEvents 的弃用警告,这是 react-native-web 库的内部问题,不影响功能,可以安全忽略。详见 docs/KNOWN_ISSUES.md。
💡 开发提示
- 开发时使用
pnpm start即可,支持快速刷新(Fast Refresh) - 只有在测试热更新功能时才需要构建真实应用
- 热更新只能更新 JavaScript/TypeScript 代码,不能更新原生代码
- 建议使用 Git 管理代码版本,方便回滚
- 每次发布更新时添加清晰的
--message说明,便于追踪
📚 相关资源
官方文档
学习资源
社区
📚 项目文档
更多详细文档请查看 docs 目录:
祝你开发愉快! 🎉