# React Native Expo 热更新项目 > 基于 Expo Router 和 EAS Update 的完整 React Native 项目,支持热更新(OTA)功能 [![Expo](https://img.shields.io/badge/Expo-~54.0-000020?style=flat&logo=expo)](https://expo.dev/) [![React Native](https://img.shields.io/badge/React%20Native-0.81-61DAFB?style=flat&logo=react)](https://reactnative.dev/) [![TypeScript](https://img.shields.io/badge/TypeScript-~5.9-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/) [![pnpm](https://img.shields.io/badge/pnpm-latest-F69220?style=flat&logo=pnpm)](https://pnpm.io/) ## 📖 目录 - [项目特性](#-项目特性) - [快速开始](#-快速开始) - [项目结构](#-项目结构) - [热更新使用指南](#-热更新使用指南) - [开发指南](#-开发指南) - [配置说明](#-配置说明) - [常见问题](#-常见问题) - [相关资源](#-相关资源) ## ✨ 项目特性 ### 核心特性 - 🎯 **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 类型定义 ## 🚀 快速开始 ### 前置要求 - **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 # 完整示例页面 🎯 │ │ ├── paper.tsx # React Native Paper 示例 │ │ └── _layout.tsx # 标签布局 │ ├── test-page.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-page.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 组件 | ✅ | ### 快速开始 1. **查看完整示例** - 运行应用,点击 "Demo" tab 🎯 2. **阅读文档** - 查看 [docs/](./docs/) 目录中的文档 3. **使用工具** - 从各个模块目录导入所需的工具 ```typescript // ✅ 推荐:从模块目录导入 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 ```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)** - 详细的工具库使用方法和示例 - **[存储系统使用指南](./docs/STORAGE_GUIDE.md)** - AsyncStorage 和 SessionStorage 使用 🎯 --- **祝你开发愉快!** 🎉 如有问题,请查看 [常见问题](#-常见问题) 或访问 [Expo 官方文档](https://docs.expo.dev/)。