React Native中FlatList展示800个emoji的性能优化求助
React Native 800+ Emoji 列表性能优化方案
我开发了一个按类别展示emoji的React Native页面,包含约800个emoji,采用嵌套FlatList实现,但存在两个核心问题:
- emoji在设备上渲染耗时过长
- 渲染完成后屏幕操作卡顿,点击emoji响应延迟
原实现代码
import { StyleSheet, View, Text, TouchableOpacity, FlatList, SafeAreaView } from 'react-native'; import emojis from './emojis.json'; import { useState } from 'react'; export default function App() { const [isLoadingEmojis, setIsLoadingEmojis] = useState(true); const renderCategoryEmoji = (categoryEmoji) => { return ( <View key={categoryEmoji.categoryId} style={{marginBottom: 10}}> <Text style={styles.categoryLabel}>{categoryEmoji.categoryName}</Text> <FlatList data={categoryEmoji.emojis} renderItem={renderEmojiItem} keyExtractor={(item) => item.id.toString()} numColumns={7} contentContainerStyle={{paddingBottom: 100}} onLayout={() => setIsLoadingEmojis(false)} /> </View> ); } const renderEmojiItem = ({ item }) => { return ( <View> <TouchableOpacity style={{ width: 47, height: 47, margin: 1}}> <Text style={{color: 'black', fontSize: 25}}> {item.emoji} </Text> </TouchableOpacity> </View> ); } return ( <SafeAreaView style={styles.container}> <FlatList style={{margin: 15, marginBottom: 50}} data={emojis} renderItem={({ item }) => renderCategoryEmoji(item)} keyExtractor={(item) => item.categoryId.toString()} /> </SafeAreaView> ); } const styles = StyleSheet.create({ container: { flex: 1, margin: 0, marginTop: 30, display: "flex", flexDirection: "column", }, categoryLabel: { fontSize: 18, fontWeight: 'bold', marginVertical: 10, }, });
针对性优化建议
1. 替换嵌套FlatList为SectionList
嵌套FlatList会带来双重视图缓存和布局计算开销,是性能瓶颈的核心原因。改用SectionList可以在单一列表中实现分组展示,大幅减少渲染开销:
import { SectionList } from 'react-native'; import { useMemo, memo } from 'react'; export default function App() { const [isLoadingEmojis, setIsLoadingEmojis] = useState(true); // 预生成分组数据,避免每次渲染重复计算 const sections = useMemo(() => { return emojis.map(category => ({ title: category.categoryName, data: category.emojis })); }, []); // 用memo包裹子组件,避免不必要重渲染 const RenderEmojiItem = memo(({ item }) => { return ( <TouchableOpacity style={{ width: 47, height: 47, margin: 1, justifyContent: 'center', alignItems: 'center'}}> <Text style={{color: 'black', fontSize: 25}}>{item.emoji}</Text> </TouchableOpacity> ); }); return ( <SafeAreaView style={styles.container}> <SectionList sections={sections} keyExtractor={(item) => item.id.toString()} renderSectionHeader={({ section }) => ( <Text style={styles.categoryLabel}>{section.title}</Text> )} renderItem={({ item }) => <RenderEmojiItem item={item} />} numColumns={7} contentContainerStyle={{ padding: 15, paddingBottom: 50 }} onLayout={() => setIsLoadingEmojis(false)} removeClippedSubviews={true} maxToRenderPerBatch={20} windowSize={5} /> </SafeAreaView> ); }
2. 组件级优化:避免不必要重渲染
- 用
React.memo包裹RenderEmojiItem,只有当item属性变化时才重新渲染 - 用
useMemo缓存分组数据,防止每次渲染重复处理emojis数组
3. 优化列表性能属性
removeClippedSubviews={true}:自动销毁屏幕外的视图,降低内存占用maxToRenderPerBatch={20}:控制每次批量渲染的数量,避免一次性渲染过多元素导致卡顿windowSize={5}:缩小预渲染范围,减少后台渲染的视图数量
4. 简化视图层级
移除renderEmojiItem中多余的外层View,直接用TouchableOpacity作为根组件,减少视图树深度,降低渲染计算量
5. 修复加载状态逻辑
原代码中每个子FlatList的onLayout都会触发setIsLoadingEmojis(false),导致多次状态更新和重渲染。改为只在根列表的onLayout中设置一次加载完成即可。
内容的提问来源于stack exchange,提问作者araraujo
相关产品推荐
相关产品推荐

