react-native - 以高性能的方式渲染 FlatList 的视频
问题描述
我正在使用带有 expo 的 react native。我有很多视频需要渲染(有点像 TikTok)。当我获取大约 30 个视频并将它们放入renderItem
方法,它会卡住且缓慢。我正在考虑获取大量视频,但每次仅向 renderItem 方法发送 3 个视频,当用户向下滚动并到达索引 2 时,它将移动第一个索引并从获取的视频中附加第四个视频。这个想法是有一个大小为 3 的小数组,并在每次滚动时更改其中的项目,以防止一次渲染所有视频。这需要数组操作,并且每次更新视频数组时都会导致重新渲染(每次更改都会产生某种闪光 - 表示整个重新渲染)。我的问题是应该如何实现,以便从客户端的角度来看,视频之间的过渡尽可能快和干净?在平面列表中呈现视频的正确方法是什么?不会被卡住?我认为不应该那样做,必须有更好的方法。
这就是我尝试过的: // 挑战是一个来自 fetch 的数组,只是为了示例的目的而对其进行切片 // 假设它是一个包含 30 个项目的数组 const [currentVideos, setCurrentVideos] = useState([challenge.切片(0,3)]);
<FlatList
data={currentVideos}
renderItem={renderItem}
keyExtractor={(challenge, i) => challenge._id}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get("window").height - UIConsts.bottomNavbarHeight}
snapToAlignment={"start"}
decelerationRate={"fast"}
ref={(ref) => {
flatListRef.current = ref;
}}
onScrollToIndexFailed={() => alert("no such index")}
onViewableItemsChanged={onViewRef.current}
onScrollEndDrag={() => (scrollEnded.current = true)}
onScrollBeginDrag={beginDarg}
></FlatList>
useEffect(() => {
// just wanted to check on 3 videos
if (currentlyPlaying === 2) {
let temp = currentVideos;
temp.shift(); // pop the top item
temp.push(challenges[4]) // append a new one
setCurrentVideos(temp);
}
}, [currentlyPlaying]);
const onViewRef = useRef(({ viewableItems }) => {
// change playing video only after user stop dragging
scrollEnded.current && setCurrentlyPlaying(viewableItems[0]?.index);
});
解决方案
我会避免
data
在组件内部操作数组和执行业务逻辑。此外,您可以使用道具实现您想要的行为,而无需操作您的data
数组。正如官方 RN 文档中提到的FlatList 优化技术。maxToRenderPerBatch
FlatList
您应该避免在组件内部使用匿名函数和对象
properties
,将它们移到 return 语句之外,并使用useMemo
anduseCallback
钩子来避免它们在每次重新渲染时不必要的重新创建。例如,不要像这样编写代码:
const App = () => {
return (
<FlatList
keyExtractor={(challenge, i) => challenge._id}
snapToInterval={Dimensions.get('window').height - UIConsts.bottomNavbarHeight}
/>
);
};
更好的方法是将其重写为如下内容:
const App = () => {
// Because of useCallback, the keyExtractor function will be memoized and won't recreate itself on every re-render
const keyExtractor = useCallback((challenge, i) => challenge._id, []);
// useMemo is almost the same as useCallback, but it is used to return non-function types
// Defining your snapToInterval variable like this will cause it to memoize its value and it
// won't recreate itself on every re-render
const snapToInterval = useMemo(() => Dimensions.get('window').height - UIConsts.bottomNavbarHeight, []);
return (
<FlatList
keyExtractor={keyExtractor}
snapToInterval={snapToInterval}
/>
);
};
如果您还没有,您应该考虑将从renderItem
函数返回的组件提取到不同的文件并应用React.memo
到它。
注意:尽量不要过度使用useCallback
和useMemo
. 您可以在此处和此处找到关于为什么不过度使用它们的详细说明。
- 如果可以的话,您应该在将视频上传到服务器之前对其进行优化。您可以根据需要优化应用程序的客户端部分,但如果内容没有得到适当优化,无论您如何努力,都无法获得流畅和高性能的体验。
这里还有一些描述如何优化FlatList
组件的文章:
- 我是如何优化我的 React Native FlatList 的?
- 优化 React 原生 FlatList 性能的 8 种方法
- 使用许多子组件优化 React Native FlatList
- 使用 Hooks 进行 React Native 性能优化
- React Native:优化的 FlatList 视频
我希望其中的一些内容对您有所帮助。祝你好运。
推荐阅读
- c# - C# - 将 DataReader 转换为 DataTable
- python-3.x - 创建用于修改图例的 GlyphRenderer
- java - 通过 Jackson/Spring REST 资源反序列化枚举字段
- entity-framework-core - 通过 EF Core 从数据库生成 C# 类
- flask - Flask 不适用于 Chrome,但适用于 Safari
- macos - Makefile 找不到可执行文件 - 没有这样的文件或目录
- shacl - Shacl sh:qualifiedValueShape 表达式缩写
- sql - 为什么在 case 命令中“不工作”?
- javascript - 如何在垂直列表中显示数组输出?
- vue.js - 如何禁用 vue-simple-calendar 中的特定日期?