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
相关产品推荐
相关产品推荐

