mcpField
R
react-native

react-native

github.com/s-hiraoku开发1972 次安装4 周前更新
代码仓库

关于 react-native

面向 React Native 和 Expo 开发者的性能模式集合,按影响程度讲解列表渲染、动画、导航、界面、状态管理及 monorepo 依赖放置。

主要功能

  1. 01FlashList 与记忆化列表项
  2. 02稳定回调与适当尺寸的图片
  3. 03适合 GPU 的动画模式
  4. 04原生导航与界面组件
  5. 05基于选择器的状态订阅
  6. 06Monorepo 依赖放置指南

适用场景

  1. 01改善大型列表的滚动表现
  2. 02减少动画卡顿
  3. 03选择原生导航与菜单
  4. 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