React Native 性能优化列表

FreeGuideOnline 最新 2026-07-12

jsx // ❌ 不良实践 {data.map(item => ( ))}


`ScrollView` 会一次性渲染所有子元素,即使它们不在屏幕可见区域内。当列表有几百条数据时,内存和 CPU 会瞬间飙升。

**`FlatList`** 是 React Native 提供的专门为长列表设计的组件。它只渲染当前可见区域的少量元素(懒加载),并且支持大量性能优化属性。

```jsx
// ✅ 推荐做法
<FlatList
  data={data}
  renderItem={({ item }) => <MyComponent item={item} />}
  keyExtractor={item => item.id}
/>

3. 掌握 FlatList 的关键性能属性

FlatList 提供了多个直接影响性能的配置项,很多开发者从未调整过,这恰恰是优化的金矿。

属性 作用 推荐值或策略
windowSize 渲染窗口的尺寸,以当前屏幕高度为单位,默认是 21(10 屏之上,10 屏之下) 大幅缩小到 5~10,减少离屏渲染量
maxToRenderPerBatch 每次批量渲染的最大元素数 从默认 10 减小到 5,避免一次处理过多组件
initialNumToRender 初始渲染的元素数量 刚好铺满一屏即可,例如 6~8
removeClippedSubviews 是否移除屏幕外子组件的原生视图(仅 Android 有效,iOS 也已支持) 开启 true,可以显著释放原生内存
getItemLayout 如果所有列表项高度固定,预先提供高度信息,避免动态测量 固定高度时必须提供,性能提升巨大
keyExtractor 用于生成唯一 key,帮助列表确定元素变化 必须提供稳定且唯一的 key,严禁用 index

3.1 windowSize 调优

windowSize 决定了 FlatList 在屏幕外渲染多少屏的内容。默认为 21,意味着它会渲染当前屏幕上方 10 屏和下方 10 屏的内容,这太激进了。将其设为 5(即上下各保留 5 屏)通常足够流畅。

<FlatList
  windowSize={5}
  // ...
/>

3.2 getItemLayout 消除测量开销

如果你的列表项高度是固定的(例如每条高度 100),一定要提供 getItemLayout。这样 FlatList 无需渲染每一个项再去测量高度,跳过布局计算,滚动时直接计算出偏移量。

const ITEM_HEIGHT = 100;

<FlatList
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
  // ...
/>

如果列表项高度不固定,可以使用 onScrollToIndexFailed 等处理异常,或者采用 FlashList 这类支持动态高度的库。

3.3 removeClippedSubviews

该属性开启后,超出渲染窗口的组件会从原生视图树中移除(相当于在原生端做了 removeFromSuperview),可以明显减少内存占用和原生绘制压力。

<FlatList
  removeClippedSubviews={true}
  // ...
/>

4. 优化列表项组件:杜绝无意义渲染

即使 FlatList 的属性设置得当,如果列表项组件本身频繁重渲染,性能也会大打折扣。每次 renderItem 被调用时,都会产生新的 JSX,可能触发整个子组件的重新渲染。

4.1 使用 React.memo 包裹 Item 组件

将列表项提取为独立组件,并使用 React.memo 进行浅比较,防止 props 未变化时的渲染。

const ListItem = React.memo(({ item, onPress }) => {
  return (
    <TouchableOpacity onPress={() => onPress(item.id)}>
      <Text>{item.title}</Text>
    </TouchableOpacity>
  );
});

4.2 避免在 renderItem 中创建内联函数或对象

renderItem 里直接写箭头函数或字面量,每次渲染都会创建新的引用,导致 React.memo 失效。

// ❌ 差劲写法
<FlatList
  renderItem={({ item }) => (
    <ListItem
      item={item}
      onPress={() => handlePress(item.id)}  // 每次都是新函数
      style={{ margin: 10 }}                // 每次都是新对象
    />
  )}
/>

// ✅ 优化方案:使用 useCallback 和外部常量
const onPressItem = useCallback((id) => {
  // 处理点击
}, []);

// 将静态样式提到组件外部
const itemStyle = { margin: 10 };

<FlatList
  renderItem={({ item }) => (
    <ListItem
      item={item}
      onPress={onPressItem}
      style={itemStyle}
    />
  )}
/>

对于包含多个回调的场景,可以使用 useCallback 并依赖 item id,或者用 dispatch 模式,确保传入的回调引用稳定。

4.3 确保 keyExtractor 的唯一和稳定

很多教程用 index 作为 key,这在列表数据被重新排序、过滤或增删时会导致组件状态错乱和性能问题。务必为每一条数据提供唯一的 ID。

keyExtractor={item => item.id.toString()}

5. 图片和媒体加载优化

列表中的图片往往是性能的最大瓶颈:解码图片、加载网络资源都会消耗大量内存和 CPU。滚动时如果大量图片同时加载,必然导致卡顿。

5.1 使用快速图片库

React Native 内置的 Image 组件在加载网络图片和缓存方面表现一般。推荐使用 react-native-fast-image,它利用原生端强大的缓存机制,加载速度更快,并可设置优先级。

import FastImage from 'react-native-fast-image';

<FastImage
  style={{ width: 80, height: 80 }}
  source={{ uri: item.avatar, priority: FastImage.priority.normal }}
  resizeMode={FastImage.resizeMode.cover}
/>

5.2 懒加载与占位图

只在列表项进入可见区域时才加载图片。FlatList 提供了 onViewableItemsChanged 回调,可以结合状态控制图片的加载,但更简单的是使用 react-native-lazy-load 之类的库,或利用 FastImageloading 属性结合骨架屏。

至少要为每张图设置固定尺寸和占位背景,避免加载完成后引起的布局跳动。

<FastImage
  style={{ width: 80, height: 80, backgroundColor: '#E1E9EE' }}
  source={{ uri: item.avatar }}
/>

5.3 滚动停止后再加载图片

当用户快速滑动列表时,可以暂停图片的加载,等列表停止滚动后再开始。这能显著提升滚动体验。

import { InteractionManager } from 'react-native';

const [shouldLoadImage, setShouldLoadImage] = useState(false);

// 在 onMomentumScrollEnd 或使用 onScroll 节流
onMomentumScrollEnd={() => {
  InteractionManager.runAfterInteractions(() => {
    setShouldLoadImage(true);
  });
}}

6. 分页加载与滚动事件节流

大数据集不可能一次性全部加载,使用分页是必然选择。结合 onEndReachedonEndReachedThreshold 实现无限滚动。

<FlatList
  data={data}
  onEndReached={loadMore}
  onEndReachedThreshold={0.5}
  // 注意:onEndReached 可能被多次触发,需要加锁
/>

优化点:

  • 在触发 loadMore 时立刻设置标志位,避免重复请求。
  • 滚动事件的回调(若需要)必须节流,比如每 16ms 处理一次,避免高频触发状态更新。
import { throttle } from 'lodash';

const handleScroll = throttle((event) => {
  // 处理滚动位置
}, 16);

7. 进阶:使用高性能列表库

如果 FlatList 优化到极致仍不能满足需求(例如秒杀场景、聊天消息列表、动态高度),可以迁移到更专业的列表库。

7.1 FlashList(推荐)

由 Shopify 开发,基于回收机制,比 FlatList 快 5~10 倍。它重用已卸载的列表项组件,极大减少 JS 线程负载,且支持动态高度、粘性头部等。

npm install @shopify/flash-list

使用上与 FlatList 类似,只需额外提供 estimatedItemSize(估算高度)。

import { FlashList } from "@shopify/flash-list";

<FlashList
  data={data}
  renderItem={({ item }) => <ListItem item={item} />}
  estimatedItemSize={200}
/>