react-native
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Native Patterns
React Native 开发模式
Performance and architecture patterns for React Native + Expo apps. Rules ranked by impact — fix CRITICAL before touching MEDIUM.
This is a starting point. The skill will grow as you build more mobile apps.
适用于React Native + Expo应用的性能与架构模式。规则按影响程度排序——优先修复CRITICAL(严重)问题,再处理MEDIUM(中等)问题。
这是一个起点,随着你开发更多移动应用,这些技巧也会不断扩充。
When to Apply
适用场景
- Building new React Native or Expo apps
- Optimising list and scroll performance
- Implementing animations
- Reviewing mobile code for performance issues
- Setting up a new Expo project
- 构建新的React Native或Expo应用
- 优化列表与滚动性能
- 实现动画效果
- 评审移动应用代码以排查性能问题
- 搭建新的Expo项目
1. List Performance (CRITICAL)
1. 列表性能(CRITICAL)
Lists are the #1 performance issue in React Native. A janky scroll kills the entire app experience.
| Pattern | Problem | Fix |
|---|---|---|
| ScrollView for data | | Use |
| Missing keyExtractor | FlatList without | |
| Complex renderItem | Expensive component in renderItem re-renders on every scroll | Wrap in |
| Inline functions in renderItem | | Extract handler: |
| No getItemLayout | FlatList measures every item on scroll (expensive) | Provide |
| FlashList | FlatList is good, FlashList is better for large lists | |
| Large images in lists | Full-res images decoded on main thread | Use |
列表是React Native中排名第一的性能问题。卡顿的滚动会彻底毁掉应用体验。
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 用ScrollView展示数据 | | 使用 |
| 缺少keyExtractor | FlatList未配置 | 设置 |
| 复杂的renderItem | renderItem中的昂贵组件会在每次滚动时重渲染 | 用 |
| renderItem中的内联函数 | | 提取处理函数: |
| 未配置getItemLayout | FlatList在滚动时会测量每个列表项(性能开销大) | 为固定高度的列表项提供 |
| FlashList | FlatList表现不错,但FlashList在处理大型列表时更出色 | |
| 列表中包含大尺寸图片 | 全分辨率图片会在主线程解码 | 使用 |
FlatList Checklist
FlatList 检查清单
Every FlatList should have:
tsx
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={renderItem} // Memoised component
getItemLayout={getItemLayout} // If items are fixed height
initialNumToRender={10} // Don't render 100 items on mount
maxToRenderPerBatch={10} // Batch size for off-screen rendering
windowSize={5} // How many screens to keep in memory
removeClippedSubviews={true} // Unmount off-screen items (Android)
/>每个FlatList都应配置以下属性:
tsx
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={renderItem} // 已记忆化的组件
getItemLayout={getItemLayout} // 若列表项高度固定
initialNumToRender={10} // 初始化时不要渲染100个项
maxToRenderPerBatch={10} // 屏幕外渲染的批次大小
windowSize={5} // 内存中保留的屏幕数量
removeClippedSubviews={true} // 卸载屏幕外的列表项(Android)
/>2. Animations (HIGH)
2. 动画(HIGH)
Native animations run on the UI thread. JS animations block the JS thread and cause jank.
| Pattern | Problem | Fix |
|---|---|---|
| Animated API for complex animations | | Use |
| Layout animation | Item appears/disappears with no transition | |
| Shared element transitions | Navigate between screens, element teleports | |
| Gesture + animation | Drag/swipe feels laggy | |
| Measuring layout | | Use |
原生动画在UI线程运行。JS动画会阻塞JS线程并导致卡顿。
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 用Animated API实现复杂动画 | | 使用 |
| 布局动画 | 列表项出现/消失时无过渡效果 | |
| 共享元素过渡 | 页面跳转时,元素突兀地移动 | 使用 |
| 手势+动画 | 拖拽/滑动操作感觉延迟 | |
| 测量布局 | | 使用 |
Reanimated Basics
Reanimated 基础示例
tsx
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
function AnimatedBox() {
const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: withSpring(offset.value) }],
}));
return (
<GestureDetector gesture={panGesture}>
<Animated.View style={[styles.box, style]} />
</GestureDetector>
);
}tsx
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
function AnimatedBox() {
const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: withSpring(offset.value) }],
}));
return (
<GestureDetector gesture={panGesture}>
<Animated.View style={[styles.box, style]} />
</GestureDetector>
);
}3. Navigation (HIGH)
3. 导航(HIGH)
| Pattern | Problem | Fix |
|---|---|---|
| Expo Router | File-based routing (like Next.js) for React Native | |
| Heavy screens on stack | Every screen stays mounted in the stack | Use |
| Deep linking | App doesn't respond to URLs | Expo Router handles this automatically. For bare RN: |
| Tab badge updates | Badge count doesn't update when tab is focused | Use |
| Navigation state persistence | App loses position on background/kill | |
| 模式 | 问题 | 修复方案 |
|---|---|---|
| Expo Router | 类Next.js的文件式路由,适用于React Native | 采用 |
| 栈中包含重负载页面 | 栈中的每个页面都会保持挂载状态 | 对无需持久化的页面设置 |
| 深度链接 | 应用对URL无响应 | Expo Router会自动处理。对于裸RN项目:配置 |
| Tab徽章更新 | 切换到标签页时徽章计数未更新 | 使用 |
| 导航状态持久化 | 应用进入后台/被杀死后丢失当前位置 | |
Expo Router Structure
Expo Router 目录结构
app/
├── _layout.tsx # Root layout (tab navigator)
├── index.tsx # Home tab
├── (tabs)/
│ ├── _layout.tsx # Tab bar config
│ ├── home.tsx
│ ├── search.tsx
│ └── profile.tsx
├── [id].tsx # Dynamic route
└── modal.tsx # Modal routeapp/
├── _layout.tsx # 根布局(标签导航器)
├── index.tsx # 首页标签
├── (tabs)/
│ ├── _layout.tsx # 标签栏配置
│ ├── home.tsx
│ ├── search.tsx
│ └── profile.tsx
├── [id].tsx # 动态路由
└── modal.tsx # 模态路由4. UI Patterns (HIGH)
4. UI模式(HIGH)
| Pattern | Problem | Fix |
|---|---|---|
| Safe area | Content under notch or home indicator | |
| Keyboard avoidance | Form fields hidden behind keyboard | |
| Platform-specific code | iOS and Android need different behaviour | |
| Status bar | Status bar overlaps content or wrong colour | |
| Touch targets | Buttons too small to tap | Minimum 44x44pt. Use |
| Haptic feedback | Taps feel dead | |
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 安全区域 | 内容被刘海或Home指示器遮挡 | 使用 |
| 键盘避让 | 表单字段被键盘遮挡 | |
| 平台专属代码 | iOS和Android需要不同的行为 | |
| 状态栏 | 状态栏与内容重叠或颜色错误 | 在根布局中使用 |
| 触摸目标 | 按钮过小难以点击 | 最小尺寸为44x44pt。使用 |
| 触觉反馈 | 点击操作无反馈感 | |
5. Images and Media (MEDIUM)
5. 图片与媒体(MEDIUM)
| Pattern | Problem | Fix |
|---|---|---|
| Image component | | Use |
| Remote images without dimensions | Layout shift when image loads | Always specify |
| Large images | OOM crashes on Android | Resize server-side or use |
| SVG | SVG support isn't native | |
| Video | Video playback | |
| 模式 | 问题 | 修复方案 |
|---|---|---|
| Image组件 | react-native的 | 使用 |
| 未指定尺寸的远程图片 | 图片加载时布局偏移 | 始终指定 |
| 大尺寸图片 | Android上出现内存不足崩溃 | 在服务端调整尺寸,或使用 |
| SVG | 原生不支持SVG | |
| 视频播放 | 实现视频播放功能 | 使用 |
6. State and Data (MEDIUM)
6. 状态与数据(MEDIUM)
| Pattern | Problem | Fix |
|---|---|---|
| AsyncStorage for complex data | JSON parse/stringify on every read | Use MMKV ( |
| Global state | Redux/MobX boilerplate for simple state | Zustand — minimal, works great with React Native |
| Server state | Manual fetch + loading + error + cache | TanStack Query — same as web, works in React Native |
| Offline first | App unusable without network | TanStack Query |
| Deep state updates | Spread operator hell for nested objects | Immer via Zustand: |
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 用AsyncStorage存储复杂数据 | 每次读取都要进行JSON解析/序列化 | 使用MMKV( |
| 全局状态 | Redux/MobX对于简单状态来说样板代码过多 | Zustand——轻量,非常适合React Native |
| 服务端状态 | 手动处理请求+加载+错误+缓存 | TanStack Query——与Web端用法一致,适用于React Native |
| 离线优先 | 无网络时应用无法使用 | TanStack Query的 |
| 深度状态更新 | 嵌套对象的展开运算符地狱 | 通过Zustand使用Immer: |
7. Expo Workflow (MEDIUM)
7. Expo 工作流(MEDIUM)
| Pattern | When | How |
|---|---|---|
| Development build | Need native modules | |
| Expo Go | Quick prototyping, no native modules | |
| EAS Build | CI/CD, app store builds | |
| EAS Update | Hot fix without app store review | |
| Config plugins | Modify native config without ejecting | |
| Environment variables | Different configs per build | |
| 模式 | 适用场景 | 实现方式 |
|---|---|---|
| 开发构建 | 需要使用原生模块时 | |
| Expo Go | 快速原型开发,无需原生模块 | |
| EAS Build | CI/CD、应用商店构建 | |
| EAS Update | 无需应用商店审核的热修复 | |
| 配置插件 | 无需 eject 即可修改原生配置 | 在 |
| 环境变量 | 不同构建使用不同配置 | |
New Project Setup
新项目搭建步骤
bash
npx create-expo-app my-app --template tabs
cd my-app
npx expo install expo-image react-native-reanimated react-native-gesture-handler react-native-safe-area-contextbash
npx create-expo-app my-app --template tabs
cd my-app
npx expo install expo-image react-native-reanimated react-native-gesture-handler react-native-safe-area-context8. Testing (LOW-MEDIUM)
8. 测试(LOW-MEDIUM)
| Tool | For | Setup |
|---|---|---|
| Jest | Unit tests, hook tests | Included with Expo by default |
| React Native Testing Library | Component tests | |
| Detox | E2E tests on real devices/simulators | |
| Maestro | E2E with YAML flows | |
| 工具 | 用途 | 搭建方式 |
|---|---|---|
| Jest | 单元测试、Hook测试 | Expo默认已包含 |
| React Native Testing Library | 组件测试 | 安装 |
| Detox | 真机/模拟器上的端到端测试 | |
| Maestro | 基于YAML流程的端到端测试 | |
Common Gotchas
常见陷阱
| Gotcha | Fix |
|---|---|
| Metro bundler cache | |
| Pod install issues (iOS) | |
| Reanimated not working | Must be first import: |
| Expo SDK upgrade | |
| Android build fails | Check |
| iOS simulator slow | Use physical device for performance testing — simulator doesn't reflect real perf |
| 陷阱 | 修复方案 |
|---|---|
| Metro打包器缓存 | |
| iOS Pod安装问题 | |
| Reanimated无法工作 | 必须作为第一个导入项:在根文件中 |
| Expo SDK升级 | 更新SDK版本后执行 |
| Android构建失败 | 检查 |
| iOS模拟器运行缓慢 | 使用真机进行性能测试——模拟器无法反映真实性能 |