react-native
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Native
React Native
Performance-first patterns for React Native and Expo, organized by impact.
React Native与Expo的性能优先实践指南,按影响优先级排序。
Priority 1: List Performance (CRITICAL)
优先级1:列表性能(关键)
Lists are the #1 performance bottleneck in RN apps. Get these right first.
列表是RN应用中排名第一的性能瓶颈,需优先优化。
Use FlashList
使用FlashList
Replace with for large lists.
FlatList@shopify/flash-listtsx
import { FlashList } from '@shopify/flash-list';
<FlashList
data={items}
renderItem={({ item }) => <ItemRow item={item} />}
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>对于大型列表,使用替代。
@shopify/flash-listFlatListtsx
import { FlashList } from '@shopify/flash-list';
<FlashList
data={items}
renderItem={({ item }) => <ItemRow item={item} />}
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>Memoize List Items
列表项记忆化处理
Every list item must be memoized.
tsx
const ItemRow = memo(function ItemRow({ item }: { item: Item }) {
return (
<View style={styles.row}>
<Text>{item.title}</Text>
</View>
);
});每个列表项都必须进行记忆化处理。
tsx
const ItemRow = memo(function ItemRow({ item }: { item: Item }) {
return (
<View style={styles.row}>
<Text>{item.title}</Text>
</View>
);
});Stabilize Callbacks
稳定回调函数
Extract callbacks and avoid inline objects in list items.
tsx
// BAD: New function + new style object every render
<Pressable onPress={() => onSelect(item.id)} style={{ padding: 16 }}>
// GOOD: Stable references
const handlePress = useCallback(() => onSelect(item.id), [item.id, onSelect]);
<Pressable onPress={handlePress} style={styles.pressable}>提取回调函数,避免在列表项中使用内联对象。
tsx
// 不良实践:每次渲染都会创建新函数和新样式对象
<Pressable onPress={() => onSelect(item.id)} style={{ padding: 16 }}>
// 良好实践:使用稳定引用
const handlePress = useCallback(() => onSelect(item.id), [item.id, onSelect]);
<Pressable onPress={handlePress} style={styles.pressable}>Optimize Images in Lists
优化列表中的图片
Use with proper sizing and caching.
expo-imagetsx
import { Image } from 'expo-image';
<Image
source={{ uri: item.thumbnailUrl }}
style={styles.thumbnail}
contentFit="cover"
placeholder={item.blurhash}
transition={200}
recyclingKey={item.id}
/>使用并配置合适的尺寸与缓存策略。
expo-imagetsx
import { Image } from 'expo-image';
<Image
source={{ uri: item.thumbnailUrl }}
style={styles.thumbnail}
contentFit="cover"
placeholder={item.blurhash}
transition={200}
recyclingKey={item.id}
/>Item Types for Heterogeneous Lists
异构列表的项类型配置
Use to help FlashList reuse cells efficiently.
getItemTypetsx
<FlashList
data={mixedItems}
renderItem={renderItem}
getItemType={(item) => item.type} // 'header' | 'content' | 'ad'
estimatedItemSize={100}
/>使用帮助FlashList高效复用单元格。
getItemTypetsx
<FlashList
data={mixedItems}
renderItem={renderItem}
getItemType={(item) => item.type} // 'header' | 'content' | 'ad'
estimatedItemSize={100}
/>Priority 2: Animation (HIGH)
优先级2:动画(高)
GPU-Only Properties
仅使用GPU支持的属性
Only animate and . Everything else triggers layout.
transformopacitytsx
import Animated, { useAnimatedStyle, withSpring } from 'react-native-reanimated';
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: withSpring(isPressed.value ? 0.95 : 1) }],
opacity: withSpring(isVisible.value ? 1 : 0),
}));仅对和属性执行动画,其他属性会触发布局重排。
transformopacitytsx
import Animated, { useAnimatedStyle, withSpring } from 'react-native-reanimated';
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: withSpring(isPressed.value ? 0.95 : 1) }],
opacity: withSpring(isVisible.value ? 1 : 0),
}));Derived Values
派生值
Use for computed animations to avoid redundant calculations.
useDerivedValuetsx
const progress = useSharedValue(0);
const rotation = useDerivedValue(() => `${progress.value * 360}deg`);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: rotation.value }],
}));使用处理计算型动画,避免冗余计算。
useDerivedValuetsx
const progress = useSharedValue(0);
const rotation = useDerivedValue(() => `${progress.value * 360}deg`);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: rotation.value }],
}));Gesture Handling
手势处理
Use for 60fps gesture tracking.
react-native-gesture-handlertsx
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const pan = Gesture.Pan()
.onUpdate((e) => {
translateX.value = e.translationX;
translateY.value = e.translationY;
})
.onEnd(() => {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
});
// Use Gesture.Tap() instead of Pressable for animated press feedback
const tap = Gesture.Tap()
.onBegin(() => { scale.value = withSpring(0.95); })
.onFinalize(() => { scale.value = withSpring(1); });使用实现60fps的手势追踪。
react-native-gesture-handlertsx
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const pan = Gesture.Pan()
.onUpdate((e) => {
translateX.value = e.translationX;
translateY.value = e.translationY;
})
.onEnd(() => {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
});
// 使用Gesture.Tap()替代Pressable实现带动画的按压反馈
const tap = Gesture.Tap()
.onBegin(() => { scale.value = withSpring(0.95); })
.onFinalize(() => { scale.value = withSpring(1); });Priority 3: Navigation (HIGH)
优先级3:导航(高)
Native Navigators
原生导航器
Always prefer native stack and tabs over JS-based alternatives.
tsx
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
// BAD: JS-based stack (slower transitions, no native gestures)
import { createStackNavigator } from '@react-navigation/stack';
// GOOD: Native stack (native transitions + gestures)
const Stack = createNativeStackNavigator();始终优先选择原生栈和标签导航,而非基于JS的替代方案。
tsx
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
// 不良实践:基于JS的栈导航(过渡动画慢,无原生手势)
import { createStackNavigator } from '@react-navigation/stack';
// 良好实践:原生栈导航(原生过渡动画+手势支持)
const Stack = createNativeStackNavigator();Screen Options
屏幕配置
Configure headers and animations natively.
tsx
<Stack.Screen
name="Detail"
component={DetailScreen}
options={{
headerLargeTitle: true, // iOS large title
animation: 'slide_from_right',
}}
/>通过原生方式配置头部与动画效果。
tsx
<Stack.Screen
name="Detail"
component={DetailScreen}
options={{
headerLargeTitle: true, // iOS大标题
animation: 'slide_from_right',
}}
/>Priority 4: UI Patterns (HIGH)
优先级4:UI模式(高)
Safe Areas
安全区域适配
Handle safe areas correctly for all device shapes.
tsx
import { SafeAreaView } from 'react-native-safe-area-context';
// For scrollable content
<SafeAreaView edges={['top']} style={{ flex: 1 }}>
<ScrollView contentInsetAdjustmentBehavior="automatic">
{children}
</ScrollView>
</SafeAreaView>针对所有设备形态正确处理安全区域。
tsx
import { SafeAreaView } from 'react-native-safe-area-context';
// 针对可滚动内容
<SafeAreaView edges={['top']} style={{ flex: 1 }}>
<ScrollView contentInsetAdjustmentBehavior="automatic">
{children}
</ScrollView>
</SafeAreaView>Native Modals
原生模态框
Use native modal presentation instead of JS overlays.
tsx
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ presentation: 'modal' }}
/>使用原生模态框展示,而非JS实现的浮层。
tsx
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ presentation: 'modal' }}
/>Native Menus
原生菜单
Use context menus instead of custom dropdown components.
tsx
import * as ContextMenu from 'zeego/context-menu';
<ContextMenu.Root>
<ContextMenu.Trigger>
<Pressable><Text>Options</Text></Pressable>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item key="edit" onSelect={handleEdit}>
<ContextMenu.ItemTitle>Edit</ContextMenu.ItemTitle>
</ContextMenu.Item>
<ContextMenu.Item key="delete" onSelect={handleDelete} destructive>
<ContextMenu.ItemTitle>Delete</ContextMenu.ItemTitle>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>使用上下文菜单替代自定义下拉组件。
tsx
import * as ContextMenu from 'zeego/context-menu';
<ContextMenu.Root>
<ContextMenu.Trigger>
<Pressable><Text>选项</Text></Pressable>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item key="edit" onSelect={handleEdit}>
<ContextMenu.ItemTitle>编辑</ContextMenu.ItemTitle>
</ContextMenu.Item>
<ContextMenu.Item key="delete" onSelect={handleDelete} destructive>
<ContextMenu.ItemTitle>删除</ContextMenu.ItemTitle>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>Pressable Over TouchableOpacity
用Pressable替代TouchableOpacity
tsx
// BAD: Legacy touch component
<TouchableOpacity onPress={onPress}>{children}</TouchableOpacity>
// GOOD: Modern Pressable with feedback
<Pressable
onPress={onPress}
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
android_ripple={{ color: 'rgba(0,0,0,0.1)' }}
>
{children}
</Pressable>tsx
// 不良实践:旧版触摸组件
<TouchableOpacity onPress={onPress}>{children}</TouchableOpacity>
// 良好实践:带反馈的现代Pressable组件
<Pressable
onPress={onPress}
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
android_ripple={{ color: 'rgba(0,0,0,0.1)' }}
>
{children}
</Pressable>Priority 5: State Management (MEDIUM)
优先级5:状态管理(中)
Minimize Re-renders
最小化重渲染
Subscribe only to the state you need.
tsx
// BAD: Re-renders on any store change
const store = useStore();
return <Text>{store.user.name}</Text>;
// GOOD: Selector extracts only needed value
const name = useStore((s) => s.user.name);
return <Text>{name}</Text>;仅订阅所需的状态数据。
tsx
// 不良实践:状态库任何变化都会触发重渲染
const store = useStore();
return <Text>{store.user.name}</Text>;
// 良好实践:使用选择器仅提取所需值
const name = useStore((s) => s.user.name);
return <Text>{name}</Text>;React Compiler Compatibility
React编译器兼容性
When using React Compiler with Reanimated:
tsx
// Destructure shared value functions for compiler compatibility
const { value } = useSharedValue(0);
// Use worklet directive for Reanimated callbacks
const animatedStyle = useAnimatedStyle(() => {
'worklet';
return { opacity: value };
});当与React Compiler配合使用Reanimated时:
tsx
// 解构共享值函数以兼容编译器
const { value } = useSharedValue(0);
// 为Reanimated回调添加worklet指令
const animatedStyle = useAnimatedStyle(() => {
'worklet';
return { opacity: value };
});Priority 6: Monorepo (MEDIUM)
优先级6:Monorepo(中)
Native Dependencies
原生依赖管理
Keep native dependencies in the app package, not shared packages.
packages/
ui/ # Pure React components (no native deps)
shared/ # Business logic, types
apps/
mobile/ # Native deps (expo-image, reanimated) here将原生依赖保留在应用包中,而非共享包。
packages/
ui/ # 纯React组件(无原生依赖)
shared/ # 业务逻辑、类型定义
apps/
mobile/ # 原生依赖(expo-image, reanimated)存放在此Single Dependency Versions
单一依赖版本
Enforce one version per dependency across the monorepo.
json
// Root package.json
{
"resolutions": {
"react-native": "0.76.x",
"react-native-reanimated": "3.x"
}
}在整个monorepo中强制每个依赖仅使用一个版本。
json
// 根目录package.json
{
"resolutions": {
"react-native": "0.76.x",
"react-native-reanimated": "3.x"
}
}Quick Reference
快速参考
| Issue | Fix | Priority |
|---|---|---|
| Slow scrolling lists | FlashList + memoized items | CRITICAL |
| Inline objects in lists | Extract to StyleSheet | CRITICAL |
| Janky animations | Only transform/opacity | HIGH |
| JS-based navigation | Native stack/tabs | HIGH |
| Custom dropdown menus | Native context menus | HIGH |
| Full store subscription | Selectors | MEDIUM |
| Native deps in shared pkg | Move to app package | MEDIUM |
| 问题 | 解决方案 | 优先级 |
|---|---|---|
| 列表滚动缓慢 | FlashList + 记忆化列表项 | 关键 |
| 列表中使用内联对象 | 提取至StyleSheet | 关键 |
| 动画卡顿 | 仅动画transform/opacity属性 | 高 |
| 基于JS的导航 | 使用原生栈/标签导航 | 高 |
| 自定义下拉菜单 | 使用原生上下文菜单 | 高 |
| 全状态库订阅 | 使用选择器 | 中 |
| 共享包中包含原生依赖 | 移至应用包 | 中 |