You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native开发类Instagram自定义图片选择器如何缓存相机胶卷图片

解决方案

一、FlatList基础配置优化

长列表卡顿绝大多数是基础配置不合理+资源体积过大导致的,先做基础优化:

  • 给FlatList加上必要的性能属性:
    • keyExtractor:指定唯一key,避免重复渲染
    • removeClippedSubviews={true}:自动卸载超出视口的单元格
    • maxToRenderPerBatch={10}:控制单次渲染的单元格数量
    • windowSize={5}:控制视口上下预渲染的屏数,不要设置过大
    • initialNumToRender={20}:首屏渲染数量刚好覆盖屏幕即可
  • 修正memo语法错误:原有代码将比较函数放在组件返回值的逗号运算符后,完全不生效,memo的比较函数需要作为第二个参数单独传入。

二、Asset资源缓存&渲染优化

FastImage确实不支持系统相册的asset-library:///ph://协议路径,推荐用Expo官方的expo-image组件替代,它原生支持相册asset路径,同时自带内存+磁盘缓存能力,不需要额外适配。
同时不要直接加载asset对应的原图,相册原图单张普遍在3-10MB,大量渲染时内存占用会直接飙升,用MediaLibrary提供的缩略图能力生成匹配单元格尺寸的小图即可:

  • 用MediaLibrary.getAssetThumbnailAsync()方法,传入你单元格的实际尺寸,生成体积只有几KB到几十KB的缩略图
  • 用asset的id作为缓存key,把生成过的缩略图uri存在全局Map里做内存缓存,避免重复生成缩略图

三、修改后的示例代码

import { useState, useEffect, memo } from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import * as MediaLibrary from 'expo-media-library';
import { Image } from 'expo-image';

// 全局内存缓存,存asset id对应缩略图uri
const thumbnailCache = new Map();

const Asset = memo(({ asset }) => {
  const [thumbUri, setThumbUri] = useState(null);

  useEffect(() => {
    // 优先读缓存
    if (thumbnailCache.has(asset.id)) {
      setThumbUri(thumbnailCache.get(asset.id));
      return;
    }
    // 没有缓存则生成缩略图,尺寸按你的单元格大小定即可
    const getThumb = async () => {
      const thumb = await MediaLibrary.getAssetThumbnailAsync(asset.id, {
        width: 120, // 你的单元格宽度
        height: 120, // 你的单元格高度
        quality: 0.8,
      });
      thumbnailCache.set(asset.id, thumb.uri);
      setThumbUri(thumb.uri);
    };
    getThumb();
  }, [asset.id]);

  if (!thumbUri) return <View style={styles.assetPlaceholder} />;

  return (
    <View style={styles.assetItem}>
      <Image
        style={styles.assetImg}
        source={{ uri: thumbUri }}
        cachePolicy="memory-disk" // 开启内存+磁盘缓存
      />
    </View>
  );
}, (prev, next) => prev.asset.id === next.asset.id); // 正确的memo比较逻辑,用唯一id判断

export default function App() {
  const [assets, setAssets] = useState([]);

  useEffect(() => {
    const getAssets = async () => {
      const { assets: resAssets } = await MediaLibrary.getAssetsAsync({ first: 50 });
      setAssets(resAssets);
    };
    getAssets();
  }, []);

  return (
    <View style={styles.container}>
      <FlatList
        data={assets}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => <Asset asset={item} />}
        // 性能优化属性
        removeClippedSubviews={true}
        maxToRenderPerBatch={10}
        windowSize={5}
        initialNumToRender={20}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  assetItem: {
    width: '33.33%',
    aspectRatio: 1,
    padding: 1,
  },
  assetImg: {
    width: '100%',
    height: '100%',
  },
  assetPlaceholder: {
    width: '100%',
    height: '100%',
    backgroundColor: '#f1f1f1',
  }
});

额外优化提示

  • 如果你的列表是网格布局,直接用numColumns属性实现,不要自己套多列布局,能进一步减少渲染层级
  • 视频类资源可以单独加个播放图标占位,不要直接渲染视频帧,滚动结束后再加载视频缩略图

内容的提问来源于stack exchange,提问作者PietroPutelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:15:04