使用react-native-contacts包时通讯录长列表加载卡顿如何优化?
性能优化方案
当前使用KeyboardAwareScrollView配合map一次性渲染全部475条联系人节点,所有节点无论是否在可视区域都会被挂载到渲染树,占用大量内存和计算资源,是卡顿的核心原因,可按以下步骤优化:
1. 替换ScrollView为FlatList实现虚拟列表
FlatList只会渲染当前可视区域的节点,超出可视区域的节点会被回收复用,400+条数据的场景下性能提升非常明显,替换后的渲染代码示例:
import { FlatList } from 'react-native'; // 抽离列表项组件,避免不必要重渲染 const ContactItem = React.memo(({ item, onPress }) => { return ( <TouchableOpacity onPress={() => onPress(item)}> <FriendsComponent isChecked={item.isChecked} nameSurname={item.nameSurname} /> </TouchableOpacity> ) }) // 渲染部分替换为以下代码 <KeyboardAwareScrollView showsVerticalScrollIndicator={false}> <Box flex={1} mt={20}> <Box ml={10} mb={5}> <Text>Total: {this.state.contacts?.length} </Text> </Box> <FlatList data={this.state.contactsTemp} keyExtractor={(item) => item.recordID} renderItem={({ item }) => <ContactItem item={item} onPress={this.checkContact} />} removeClippedSubviews={true} // 按实际FriendsComponent高度调整数值,提前固定布局避免动态计算 getItemLayout={(data, index) => ({length: 60, offset: 60 * index, index})} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={5} /> </Box> </KeyboardAwareScrollView>
keyExtractor不要用index作为唯一标识,用唯一的recordID可以大幅提升列表节点复用效率。
2. 优化数据处理逻辑
现有数据处理代码存在冗余计算和逻辑漏洞,优化后可减少初始化耗时:
getContacts() { // 封装公共处理逻辑,避免两端重复代码 const processContacts = (rawContacts, nameKey) => { return rawContacts.map(contact => ({ nameSurname: contact[nameKey], nameSurnameLower: contact[nameKey].toLowerCase(), // 提前存储小写姓名,避免排序时重复计算 recordID: contact.recordID, phone: contact.phoneNumbers[0], isChecked: false, })).sort((a, b) => a.nameSurnameLower.localeCompare(b.nameSurnameLower)) } if (Platform.OS == 'android') { PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_CONTACTS, { title: 'Contacts', message: 'This app would like to view your contacts.', buttonPositive: 'Please accept bare mortal', }).then(grantResult => { // 权限授权成功后再获取通讯录,避免权限被拒绝时无效执行 if (grantResult === PermissionsAndroid.RESULTS.GRANTED) { Contacts.getAllWithoutPhotos().then(contacts => { const processedContacts = processContacts(contacts, 'displayName') this.setState({ contacts: processedContacts, contactsTemp: processedContacts, loaded: true }) }) } }) } else { Contacts.getAllWithoutPhotos().then(contacts => { const processedContacts = processContacts(contacts, 'givenName') this.setState({ contacts: processedContacts, contactsTemp: processedContacts, loaded: true }) }) } }
3. 优化选中状态更新逻辑
避免选中联系人时全量重渲染整个列表,优化checkContact方法:
checkContact = (targetItem) => { const updateIsChecked = (list) => { return list.map(item => { if (item.recordID === targetItem.recordID) { return {...item, isChecked: !item.isChecked} } return item }) } this.setState({ contactsTemp: updateIsChecked(this.state.contactsTemp), contacts: updateIsChecked(this.state.contacts) }) }
4. 组件层级优化
检查FriendsComponent的实现,避免不必要的嵌套层级,组件本身用React.memo包裹,仅当props变化时才触发重渲染。
完成以上优化后,400+条联系人的滑动、选中操作流畅度会有明显提升。
内容的提问来源于stack exchange,提问作者tes krmtstk4
相关产品推荐
相关产品推荐

