|
|
4 weeks ago | |
|---|---|---|
| app | 4 weeks ago | |
| assets | 4 weeks ago | |
| components | 4 weeks ago | |
| constants | 4 weeks ago | |
| docs | 1 month ago | |
| hooks | 4 weeks ago | |
| pages | 4 weeks ago | |
| schemas | 4 weeks ago | |
| scripts | 4 weeks ago | |
| services | 4 weeks ago | |
| stores | 4 weeks ago | |
| theme | 4 weeks ago | |
| types | 4 weeks ago | |
| utils | 4 weeks ago | |
| .env.development | 1 month ago | |
| .env.example | 1 month ago | |
| .env.production | 1 month ago | |
| .gitignore | 1 month ago | |
| .prettierignore | 1 month ago | |
| .prettierrc.json | 1 month ago | |
| README.md | 4 weeks ago | |
| app.json | 1 month ago | |
| eas.json | 1 month ago | |
| metro.config.js | 1 month ago | |
| package.json | 4 weeks ago | |
| pnpm-lock.yaml | 4 weeks ago | |
| tsconfig.json | 4 weeks ago | |
README.md
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
架构特性 🎯
- 📁 扁平化目录结构 - 清晰的模块组织,符合社区最佳实践
- 🔄 模块化导出 - 每个目录独立导出,避免循环依赖
- 💾 双存储系统 - AsyncStorage(持久化)+ SessionStorage(临时)
- 🎨 主题化组件 - ThemedText 和 ThemedView,自动适配主题
- 🔐 API 加密 - 请求/响应自动加密解密
- 📊 状态管理 - Zustand + AsyncStorage 持久化
- ✅ 数据验证 - Zod 表单验证
- 🎯 类型安全 - 完整的 TypeScript 类型定义
🚀 快速开始
前置要求
第一步:安装依赖
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 # 完整示例页面 🎯
│ │ ├── paper.tsx # React Native Paper 示例
│ │ └── _layout.tsx # 标签布局
│ ├── test.tsx # 测试页面(无 tabs)🎯
│ ├── _layout.tsx # 根布局 - 自动检查更新 ⭐
│ ├── modal.tsx # 模态页面示例
│ ├── +html.tsx # Web HTML 模板
│ └── +not-found.tsx # 404 页面
│
├── 💻 业务代码目录
├── utils/ # 工具函数
│ ├── network/ # 网络相关
│ │ ├── api.ts # Axios API 配置
│ │ ├── helper.ts # 加密/解密工具
│ │ └── error.ts # 错误处理
│ ├── storage.ts # AsyncStorage 封装
│ ├── sessionStorage.ts # Session Storage 实现 🎯
│ ├── storageManager.ts # 统一存储管理器 🎯
│ ├── config.ts # 配置管理
│ ├── date.ts # Day.js 日期工具
│ ├── common.ts # 通用工具
│ └── index.ts # 统一导出
│
├── stores/ # Zustand 状态管理
│ ├── userStore.ts # 用户状态
│ ├── settingsStore.ts # 应用设置
│ ├── tenantStore.ts # 租户状态 🎯
│ └── index.ts # 统一导出
│
├── schemas/ # Zod 验证规则
│ ├── auth.ts # 认证验证
│ ├── user.ts # 用户验证
│ └── index.ts # 统一导出
│
├── services/ # API 服务层
│ ├── authService.ts # 认证服务
│ ├── userService.ts # 用户服务
│ ├── tenantService.ts # 租户服务 🎯
│ └── index.ts # 统一导出
│
├── hooks/ # 自定义 Hooks
│ ├── useDebounce.ts # 防抖 Hook
│ ├── useThrottle.ts # 节流 Hook
│ ├── useHaptics.ts # 触觉反馈 Hook
│ ├── useRequest.ts # 请求 Hook 🎯
│ ├── useTheme.ts # 主题 Hooks 🎯
│ ├── useColorScheme.ts # 颜色方案 Hook 🎯
│ ├── useColorScheme.web.ts # Web 平台颜色方案 🎯
│ ├── useClientOnlyValue.ts # 客户端值 Hook 🎯
│ ├── useClientOnlyValue.web.ts # Web 平台客户端值 🎯
│ └── index.ts # 统一导出
│
├── types/ # TypeScript 类型
│ ├── api.ts # API 类型定义
│ └── index.ts # 统一导出
│
├── screens/ # 业务页面组件 🎯
│ └── index.ts # 统一导出
│
├── 🧩 components/ # 可复用组件
│ ├── Themed.tsx # 主题化组件 🎯
│ ├── ThemeDemo.tsx # 主题演示组件 🎯
│ ├── ExternalLink.tsx # 外部链接组件
│ └── index.ts # 统一导出 🎯
│
├── 🎨 theme/ # 主题系统 🎯 NEW!
│ ├── index.ts # 统一导出
│ ├── utils.ts # 主题工具函数
│ └── styles.ts # 样式工厂(类似 CSS 类名)
│
├── 🎯 constants/ # 常量配置
│ ├── Colors.ts # 颜色主题(完整配置)🎯
│ └── network.ts # 网络常量
│
├── 🎨 assets/ # 静态资源
│ ├── images/ # 图片资源
│ └── fonts/ # 字体文件
│
├── 📚 docs/ # 项目文档 🎯
│ ├── USAGE_EXAMPLES.md # 使用示例
│ ├── LIBRARIES.md # 工具库使用指南
│ ├── PROJECT_STRUCTURE_V2.md # 项目结构说明 🎯
│ ├── MIGRATION_TO_FLAT_STRUCTURE.md # 扁平化迁移报告 🎯
│ ├── STORES_ARCHITECTURE.md # Stores 架构设计 🎯
│ ├── STORAGE_GUIDE.md # 存储系统使用指南 🎯
│ ├── THEME_GUIDE.md # 主题系统使用指南 🎯 NEW!
│ └── ... 更多文档
│
├── 🔧 scripts/ # 脚本文件
│ └── proxy-server.js # 代理服务器 🎯
│
├── ⚙️ 配置文件
│ ├── .env.development # 开发环境变量 🎯
│ ├── .env.production # 生产环境变量 🎯
│ ├── 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- 完整示例页面,展示所有工具的使用 🎯app/test.tsx- 测试页面示例(不包含底部 tabs)🎯
💻 业务代码目录(扁平化结构)🎯
项目采用扁平化目录结构,所有业务模块都在根目录下:
-
utils/- 工具函数- 网络请求(Axios + 加密)
- 存储管理(AsyncStorage + SessionStorage)🎯
- 日期处理(Day.js)
- 配置管理
-
stores/- 状态管理(Zustand)- 用户状态(登录、用户信息)
- 应用设置(主题、语言、触觉反馈)
- 租户状态 🎯
-
schemas/- 数据验证(Zod)- 认证表单验证
- 用户数据验证
-
services/- API 服务层- 认证服务
- 用户服务
- 租户服务 🎯
-
hooks/- 自定义 Hooks- 防抖/节流
- 触觉反馈
- 请求管理 🎯
- 主题 Hooks(useColorScheme, useTheme, useClientOnlyValue)🎯
-
types/- TypeScript 类型定义 -
screens/- 业务页面组件 🎯 -
theme/- 主题系统 🎯 NEW!- 统一的主题配置导出
- 主题工具函数
- 样式工厂(类似 CSS 类名)
每个目录都有 index.ts 文件,负责统一导出该目录下的所有模块。
🧩 components/ - 可复用组件
Themed.tsx- 主题化组件(ThemedText, ThemedView)🎯ThemeDemo.tsx- 主题演示组件 🎯
📚 docs/ - 项目文档 🎯
完善的项目文档,包含:
- 使用指南 - 如何使用各个工具
- 代码示例 - 实际的代码示例
- 配置说明 - 项目配置详解
- 架构设计 - Stores 架构、存储系统等
- 主题系统 - 主题配置和样式使用指南 🎯 NEW!
- 迁移报告 - 扁平化目录结构迁移
核心文件说明
热更新相关 ⭐
app.json- Expo 配置,包含热更新设置eas.json- EAS 构建和更新通道配置app/_layout.tsx- 自动检查更新逻辑app/(tabs)/index.tsx- 手动检查更新功能
工具配置 🎯
- 各目录的
index.ts- 统一导出,从这里导入所有工具 .env.development/.env.production- 环境变量配置 🎯tsconfig.json- 配置了路径别名@/*
已安装的工具库
项目已安装并配置好以下工具库:
| 类别 | 工具库 | 用途 | 状态 |
|---|---|---|---|
| 工具类 | lodash-es | JavaScript 工具函数 | ✅ |
| dayjs | 日期处理 | ✅ | |
| axios | HTTP 请求 | ✅ | |
| 状态管理 | zustand | 轻量级状态管理 | ✅ |
| 表单处理 | react-hook-form | 表单管理 | ✅ |
| zod | 数据验证 | ✅ | |
| 原生功能 | @react-native-async-storage/async-storage | 本地存储 | ✅ |
| expo-image | 优化的图片组件 | ✅ | |
| expo-haptics | 触觉反馈 | ✅ | |
| UI 组件 | react-native-paper | Material Design 组件 | ✅ |
快速开始
- 查看完整示例 - 运行应用,点击 "Demo" tab 🎯
- 阅读文档 - 查看 docs/ 目录中的文档
- 使用工具 - 从各个模块目录导入所需的工具
// ✅ 推荐:从模块目录导入
import { Storage, SessionStorage, StorageManager, formatDate } from '@/utils';
import { useUser, useTheme, useLanguage } from '@/stores';
import { authService, userService } from '@/services';
import {
useDebounce,
useHaptics,
useColorScheme,
useThemeColors,
useClientOnlyValue,
} from '@/hooks';
import { loginSchema } from '@/schemas';
// ✅ 主题系统:统一从 theme 目录导入 🎯 NEW!
import {
useColorScheme,
useThemeColors,
ThemedText,
ThemedView,
commonStyles,
createThemeStyles,
} from '@/theme';
// ✅ 也可以:直接从具体文件导入
import { useUser } from '@/stores/userStore';
import { formatDate } from '@/utils/date';
🔥 热更新使用指南
重要提示:热更新功能需要在真实构建(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 目录:
使用指南
祝你开发愉快! 🎉