本章把 React Native 知识体系落到一个渐进式综合项目:Mobile Knowledge Hub。它从最小组件开始,逐步扩展到导航、状态管理、异步数据、离线缓存、权限、性能优化、新架构和发布体系。
1. 专家能力模型
| 层级 | 能力目标 |
|---|---|
| 入门 | 能写核心组件、样式、布局、基础交互 |
| 进阶 | 能处理导航、状态、表单、数据请求、设备能力 |
| 高级 | 能优化列表、动画、图片、启动、线程卡顿 |
| 精通 | 能建立 TypeScript、测试、调试、构建、发布体系 |
| 专家 | 能设计跨平台架构、离线同步、新架构迁移和长期演进 |
2. Mobile Knowledge Hub 背景
应用目标:
- 展示 React Native 学习模块。
- 支持搜索、层级筛选、收藏、完成状态。
- 支持详情页。
- 支持离线读取。
- 支持推送学习提醒。
- 支持统计学习进度。
- 最终具备真实移动应用复杂度。
3. Stage 1:核心组件
jsx
function LessonCard({ item, onPress }) {
return (
<Pressable style={styles.card} onPress={() => onPress(item.id)}>
<Text style={styles.level}>{item.level}</Text>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.summary} numberOfLines={2}>
{item.summary}
</Text>
</Pressable>
);
}
训练点:
View。Text。Pressable。StyleSheet。- 文本截断。
- 移动端点击区域。
4. Stage 2:列表和筛选
jsx
function LessonListScreen() {
const [query, setQuery] = useState('');
const visibleLessons = useMemo(() => {
return lessons.filter((lesson) => lesson.title.includes(query));
}, [query]);
return (
<View style={styles.screen}>
<TextInput
value={query}
onChangeText={setQuery}
placeholder="搜索知识点"
/>
<FlatList
data={visibleLessons}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <LessonCard item={item} />}
/>
</View>
);
}
训练点:
- 受控输入。
FlatList。- 派生数据。
- 列表 key。
5. Stage 3:Reducer 状态模型
js
const initialState = {
query: '',
level: '全部',
favorites: [],
completed: [],
};
function reducer(state, action) {
switch (action.type) {
case 'query-changed':
return { ...state, query: action.query };
case 'favorite-toggled':
return {
...state,
favorites: state.favorites.includes(action.id)
? state.favorites.filter((id) => id !== action.id)
: [...state.favorites, action.id],
};
case 'completed-toggled':
return {
...state,
completed: state.completed.includes(action.id)
? state.completed.filter((id) => id !== action.id)
: [...state.completed, action.id],
};
default:
return state;
}
}
6. Stage 4:导航
jsx
function LessonsStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Lessons" component={LessonListScreen} />
<Stack.Screen name="LessonDetail" component={LessonDetailScreen} />
</Stack.Navigator>
);
}
跳转:
jsx
navigation.navigate('LessonDetail', { id: item.id });
详情页通过 ID 读取数据,不通过 params 传大对象。
7. Stage 5:服务端数据
jsx
function useLessons() {
return useQuery({
queryKey: ['lessons'],
queryFn: fetchLessons,
});
}
Mutation:
jsx
const mutation = useMutation({
mutationFn: updateProgress,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['lessons'] });
},
});
8. Stage 6:离线能力
离线策略:
- 首次成功请求后缓存课程。
- 收藏和完成状态本地保存。
- 离线操作进入 outbox。
- 恢复网络后同步。
- 冲突时以服务端版本或用户确认策略处理。
Outbox 概念:
ts
type OutboxAction =
| { type: 'complete-lesson'; lessonId: string; createdAt: number }
| { type: 'favorite-lesson'; lessonId: string; createdAt: number };
9. Stage 7:推送和提醒
能力:
- 请求通知权限。
- 注册设备 token。
- 服务端保存 token。
- 根据学习计划发送提醒。
- 点击通知进入对应课程。
专家注意:
- 权限拒绝要降级。
- 通知点击要接入 Deep Link。
- token 会变化,需要刷新。
- iOS/Android 行为不同。
10. Stage 8:性能优化
项目中必须处理:
FlatList参数。LessonCardmemo。- 图片尺寸和缓存。
- 搜索防抖。
- 导航后延迟重计算。
- Release 包性能验证。
jsx
const renderItem = useCallback(({ item }) => {
return <LessonCard item={item} onPress={openDetail} />;
}, [openDetail]);
11. Stage 9:工程化
最终目录:
text
src/
├── app/
│ ├── navigation/
│ └── providers/
├── features/
│ └── lessons/
│ ├── components/
│ ├── screens/
│ ├── model/
│ ├── services/
│ └── hooks/
├── shared/
│ ├── ui/
│ ├── theme/
│ ├── device/
│ └── api/
└── main.tsx
12. 架构边界
页面层:
- 读取导航参数。
- 组织屏幕布局。
- 接入页面级数据。
Feature 层:
- 业务组件。
- Reducer。
- Selector。
- 业务 Hook。
Service 层:
- API 请求。
- 本地缓存。
- 同步队列。
Shared 层:
- 基础 UI。
- theme。
- device hooks。
- 通用工具。
13. ADR 示例
md
# ADR: Mobile Knowledge Hub 使用 Expo Dev Build
## 背景
应用需要推送、相机扫码、离线存储和少量原生 SDK。
## 决策
使用 Expo Development Build,保留 Expo 工具链,同时允许自定义原生能力。
## 后果
优点:构建和发布效率高,生态能力充足。
代价:需要理解 config plugin 和 EAS 构建流程。
14. 反模式
- 长列表使用
ScrollView。 - route params 传复杂对象。
- 把权限只放在客户端判断。
- 忽略 Android 返回键。
- 输入页不处理键盘遮挡。
- 图片不设置尺寸。
- 只在 Debug 包测试性能。
- 任意引入未维护原生库。
- 不上传 source map。
- OTA 更新没有回滚策略。
15. Debug 顺序
页面异常:
- 数据是否正确。
- route params 是否正确。
- state 是否直接修改。
- FlatList key 是否稳定。
- 平台差异是否处理。
性能异常:
- Release 包是否复现。
- JS 线程是否阻塞。
- UI 线程是否掉帧。
- 列表是否过重。
- 图片是否过大。
- 原生模块是否频繁调用。
发布异常:
- 环境配置。
- 证书和签名。
- 原生权限配置。
- 版本号和 build number。
- 新架构兼容。
16. 专家检查清单
架构:
- 模块依赖方向是否清晰?
- 领域规则是否从 UI 抽离?
- 离线同步是否有冲突策略?
- Deep Link 是否覆盖关键路径?
- 权限是否由服务端最终校验?
性能:
- 长列表是否虚拟化?
- 动画是否避免 JS 每帧驱动?
- 图片是否有缩略图和缓存?
- 首屏是否延迟低频模块?
- Release 包是否验证?
工程:
- TypeScript 是否覆盖导航参数?
- E2E 是否覆盖关键路径?
- 崩溃上报和 source map 是否接入?
- OTA 是否有灰度和回滚?
- Android/iOS 真机是否都测过?
专家判断:
- 为什么选择 Expo 或 RN CLI?
- 为什么这个状态不进全局 store?
- 为什么这个模块需要原生能力?
- 新架构迁移失败如何回退?
- 当前方案 12 个月后还能否承载业务变化?
17. 移动端架构场景题
17.1 内容学习 App
特征:
- 内容列表。
- 播放或阅读。
- 收藏和进度。
- 离线缓存。
推荐:
- Expo Dev Build。
- TanStack Query。
- 本地缓存和 outbox。
- 推送提醒。
- FlatList/FlashList。
17.2 高实时协作 App
特征:
- WebSocket。
- 本地草稿。
- 冲突合并。
- 弱网复杂。
推荐:
- 明确 sync engine。
- 操作日志。
- 幂等 mutation。
- 冲突策略。
- 网络状态机。
17.3 重原生能力 App
特征:
- 相机、蓝牙、地图、音视频、传感器。
- 原生 SDK 多。
推荐:
- RN CLI 或 Expo Dev Build。
- Native Module 边界设计。
- 原生团队参与。
- 新架构兼容评估。
18. 离线优先架构
离线优先不是简单缓存。
需要:
- 本地数据源。
- 同步队列。
- 冲突解决。
- 后台同步。
- UI 标记 pending。
- 用户可理解的失败恢复。
状态模型:
ts
type SyncStatus = 'synced' | 'pending' | 'failed';
type LocalLessonProgress = {
lessonId: string;
completed: boolean;
syncStatus: SyncStatus;
updatedAt: number;
};
19. 权限和隐私架构
专家级应用要设计:
- 最小权限。
- 延迟请求权限。
- 权限用途说明。
- 用户拒绝后的降级路径。
- 隐私政策和商店声明一致。
- 敏感数据加密存储。
权限不是按钮状态,而是产品流程。
20. 主线项目最终验收
入门:
- 核心组件页面。
- 样式和布局。
- 搜索和列表。
进阶:
- Stack/Tabs 导航。
- Reducer 状态。
- 数据请求。
- 权限和设备状态。
高级:
- FlatList 优化。
- 图片优化。
- 动画和手势。
- AppState 和离线。
精通:
- TypeScript。
- 测试。
- EAS/CI。
- 崩溃监控。
- OTA。
专家:
- 新架构评估。
- Native Module 边界。
- 离线同步。
- ADR。
- 发布回滚。
21. React Native 专家总表
- Core Components。
- StyleSheet。
- Flexbox。
- Safe Area。
- Platform。
- TextInput。
- FlatList。
- SectionList。
- Navigation。
- Deep Link。
- BackHandler。
- Permissions。
- AppState。
- Linking。
- AsyncStorage。
- NetInfo。
- KeyboardAvoidingView。
- Animated。
- Reanimated。
- Gesture Handler。
- Hermes。
- Metro。
- Fabric。
- TurboModules。
- JSI。
- Codegen。
- Expo。
- EAS。
- OTA。
- Native crash。
- Source maps。
- Store release。
- 离线同步。
- 权限隐私。
- 新架构迁移。
22. React Native 架构失败信号
- 所有屏幕都直接请求接口。
- 导航参数传完整业务对象。
- 离线状态没有产品策略。
- 原生模块 API 随业务页面变化。
- 推送点击不能恢复正确页面。
- Android/iOS 行为长期不一致。
- 发布依赖某个人手工操作。
- 崩溃日志无法映射源码。
- OTA 版本和 native 版本不兼容。
- 新架构升级没有回退计划。
23. 专家决策模板
text
问题:
当前移动端复杂度来自哪里?
约束:
团队、时间、原生能力、发布要求、性能目标。
备选:
Expo / Dev Build / RN CLI / Native。
决策:
选择方案和原因。
风险:
依赖、性能、发布、兼容、审核。
回退:
如何关闭、降级或迁移。
24. Mobile Knowledge Hub 扩展路线
- v1:本地课程列表、搜索、收藏。
- v2:详情页、学习进度、Reducer。
- v3:远程数据、缓存、刷新。
- v4:离线 outbox、恢复同步。
- v5:推送提醒、Deep Link。
- v6:性能优化、FlashList、图片缓存。
- v7:E2E、崩溃上报、OTA。
- v8:新架构兼容和自定义 Native Module。
面试题完整答案总集:React Native 架构与专家实践
为什么选择 Expo、Dev Build、RN CLI 或 Native?
选择取决于原生能力复杂度、团队能力、发布要求和长期维护成本。普通业务应用优先 Expo;需要少量自定义原生能力用 Expo Dev Build;大量原生 SDK 或深度平台集成用 RN CLI;性能或平台体验要求极高的核心模块可考虑原生实现。
为什么这个状态不进全局 store?
如果状态只属于一个屏幕或 feature,全局 store 会扩大耦合和维护成本。移动端还要考虑导航生命周期、离线缓存和服务端状态。只有跨多个远距离屏幕共享、需要持久化或细粒度订阅时,才考虑全局 store。
为什么这个模块需要原生能力?
当 JS 和现有库无法满足平台 API、性能、安全、硬件或第三方 SDK 接入需求时,才需要原生模块。例如加密、生物识别、蓝牙、相机帧处理、音视频、地图 SDK。普通业务逻辑不应下沉到原生。
新架构迁移失败如何回退?
应在迁移前保留关闭 New Architecture 的构建配置,使用独立分支验证依赖兼容,CI 同时验证 iOS/Android 构建。失败时回退配置、锁定依赖版本、替换不兼容库,并通过 ADR 记录原因和后续计划。
当前方案 12 个月后还能否承载业务变化?
要评估模块边界、导航结构、状态分层、离线策略、发布流程、依赖治理和新架构兼容。如果业务增长后仍能按 feature 扩展、服务层清晰、测试覆盖关键路径、OTA 可回滚、原生能力有边界,就更能承载长期变化。
离线优先架构最关键的是什么?
关键是本地数据源、操作队列、同步协议和冲突解决,而不是简单缓存。每个离线操作都要有唯一 ID、状态、重试次数和时间戳;服务端接口要幂等;UI 要标记 pending/failed;恢复网络后要能安全同步。
权限和隐私架构如何设计?
遵循最小权限、延迟请求、用途说明、拒绝降级、敏感数据安全存储。权限不是技术弹窗,而是产品流程。服务端仍需做权限校验,客户端权限只决定能否调用设备能力。
React Native 专家和普通开发者的区别是什么?
普通开发者能写页面和调用库;专家能判断跨平台边界、性能瓶颈、原生依赖风险、发布和回滚策略、离线同步、权限隐私、新架构迁移成本,并能把这些决策文档化、可验证、可回退。