关于 react-native
面向 React Native 和 Expo 开发者的性能模式集合,按影响程度讲解列表渲染、动画、导航、界面、状态管理及 monorepo 依赖放置。
主要功能
- 01FlashList 与记忆化列表项
- 02稳定回调与适当尺寸的图片
- 03适合 GPU 的动画模式
- 04原生导航与界面组件
- 05基于选择器的状态订阅
- 06Monorepo 依赖放置指南
适用场景
- 01改善大型列表的滚动表现
- 02减少动画卡顿
- 03选择原生导航与菜单
- 04限制状态变化引发的多余重渲染
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ s-hiraoku/ synapse-a2a --skill react-native安装后在 Claude Code 中输入/react-native确认已加载。
详细介绍
React Native
以性能为先的 React Native 和 Expo 实践模式,按影响程度组织。
优先级 1:列表性能(关键)
列表是 RN 应用中最大的性能瓶颈。请先做好这些。
使用 FlashList
对于大型列表,用 @shopify/flash-list 替换 FlatList。
import { FlashList } from '@shopify/flash-list';
<FlashList
data={items}
renderItem={({ item }) => <ItemRow item={item} />}
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>
对列表项进行记忆化
每个列表项都必须进行记忆化。
const ItemRow = memo(function ItemRow({ item }: { item: Item }) {
return (
<View style={styles.row}>
<Text>{item.title}</Text>
</View>
);
});
稳定回调
提取回调,避免在列表项中使用内联对象。
// 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}>
优化列表中的图像
使用 expo-image,并设置合适的尺寸和缓存。
import { Image } from 'expo-image';
<Image
source={{ uri: item.thumbnailUrl }}
style={styles.thumbnail}
contentFit="cover"
placeholder={item.blurhash}
transition={200}
recyclingKey={item.id}
/>
异构列表的项目类型
使用 getItemType 帮助 FlashList 高效复用单元格。
<FlashList
data={mixedItems}
renderItem={renderItem}
getItemType={(item) => item.type} // 'header' | 'content' | 'ad'
estimatedItemSize={100}
/>
优先级 2:动画(高)
仅使用 GPU 属性
只对 transform 和 opacity 设置动画。其他属性都会触发布局。
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),
}));
派生值
对计算得出的动画使用 useDerivedValue,避免重复计算。
const progress = useSharedValue(0);
const rotation = useDerivedValue(() => `${progress.value * 360}deg`);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: rotation.value }],
}));
手势处理
使用 react-native-gesture-handler 实现 60fps 手势跟踪。
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); });
优先级 3:导航(高)
原生导航器
始终优先使用原生堆栈和标签页,而非基于 JS 的替代方案。
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();
屏幕选项
使用原生方式配置标题栏和动画。
<Stack.Screen
name="Detail"
component={DetailScreen}
options={{
headerLargeTitle: true, // iOS large title
animation: 'slide_from_right',
}}
/>
优先级 4:UI 模式(高)
安全区域
针对所有设备形态正确处理安全区域。
import { SafeAreaView } from 'react-native-safe-area-context';
// For scrollable content
<SafeAreaView edges={['top']} style={{ flex: 1 }}>
<ScrollView contentInsetAdjustmentBehavior="automatic">
{children}
</ScrollView>
</SafeAreaView>
原生模态框
使用原生模态框呈现方式,而非 JS 覆盖层。
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ presentation: 'modal' }}
/>
原生菜单
使用上下文菜单,而非自定义下拉组件。
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>
使用 Pressable 而非 TouchableOpacity
// 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>
优先级 5:状态管理(中)
尽量减少重新渲染
只订阅所需的状态。
// 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>;
兼容 React Compiler
将 React Compiler 与 Reanimated 搭配使用时:
// Destructure shared value functions for compiler compatibility
const { value } = useSharedValue(0);
// Use worklet directive for Reanimated callbacks
const animatedStyle = useAnimatedStyle(() => {
'worklet';
return { opacity: value };
});
优先级 6:单体仓库(中)
原生依赖
将原生依赖保留在应用包中,而非共享包中。
packages/
ui/ # Pure React components (no native deps)
shared/ # Business logic, types
apps/
mobile/ # Native deps (expo-image, reanimated) here
统一依赖版本
确保整个单体仓库中的每个依赖都使用同一个版本。
// Root package.json
{
"resolutions": {
"react-native": "0.76.x",
"react-native-reanimated": "3.x"
}
}
快速参考
| 问题 | 修复方法 | 优先级 |
|---|---|---|
| 列表滚动缓慢 | FlashList + 记忆化列表项 | 关键 |
| 列表中的内联对象 | 提取到 StyleSheet | 关键 |
| 动画卡顿 | 仅使用 transform/opacity | 高 |
| 基于 JS 的导航 | 原生堆栈/标签页 | 高 |
| 自定义下拉菜单 | 原生上下文菜单 | 高 |
| 订阅整个状态存储 | 选择器 | 中 |
| 共享包中的原生依赖 | 移至应用包 | 中 |
评论
支持 Markdown