React-Native開發中的靈異元件-列表
靈異元件
眾所周知,列表是移動開發中非常常用的元件(控制元件)。原生的列表,拿iOS來來說,有UITableView & UICollectionview,就算是長列表的情況也只是載入速度慢,不會出現突然不再載入的情況,Android的應該也是這種情況。但是React-Native中提供的列表就會出現突然不再載入的情況,嚴格來說是列表的item不再渲染了,因為檢視資料,發現列表的資料的資料是正確的。React-Native提供給開發者的列表元件有三種, FlatList
, SectionList
,ListView
,目前的使用情況來看,FlatList
& SectionList
都會出現這種靈異事件。
使用場景
國家手機號程式碼列表
由於專案的需要,目前的App需要提供一個全球的手機號程式碼列表,供使用者註冊的時候,選擇自己國家的手機號程式碼。列表見下圖:
這個列表的資料一共是242條,由於需要排序分組並且給使用者提供索引,所以採用sectionList來實現該頁面。頁面剛完成的時候,效果還算可以,除了快速滑動的時候會出現白屏,索引的跳轉不是很準確(我從網上查了一下,那兩個是React-native本省的問題),算是完成任務,就提交給策劃,然後進行新功能的開發。
實現程式碼如下:
render(){ return (<View style={styles.containStyle}> <CustomNavBar title="Country" navigator={this.props.navigator}></CustomNavBar> <SectionList ref={el=> this.sectionList=el} initialNumToRender={20} showsVerticalScrollIndicator={true} // onEndReachedThreshold={0.1} // onEndReached={()=> this.loadMoreData(false)} sections={this.state.totalSections} renderItem={this._renderItem} getItemLayout={this._getItemLayout} keyExtractor={(item: Item, index: number)=> index.toString()} extraData={this.state} renderSectionHeader={this._renderSectionHeader} ItemSeparatorComponent={()=><View style={{width: '100%', height: 1/PixelRatio.get(), backgroundColor: THEME_COLOR.SEPATOR_COLOR, marginLeft: 12, marginRight: 24}}></View>}> </SectionList> <SideBar indexs={this.state.indexs} onLetterSelectedListener={this.onSideBarSelected.bind(this)}></SideBar> </View>); } _renderItem = (info: any) => { return (<CountryCodeItem item={info.item} itemSelected={this.itemSelected.bind(this, info.item)}></CountryCodeItem>); } _getItemLayout = (data:any, index:number) => { const itemHeight = 40; return { length: itemHeight, offset: (itemHeight + 0) * index, index, }; } _renderSectionHeader = (info: any) => { let txt = info.section.key; return <Text key={txt} style={{ height:20, textAlignVertical: 'center', backgroundColor: THEME_COLOR.BOX_BOTTOM_COLOR, color: '#666666', fontSize: 12, paddingLeft: 16, paddingTop: 4}}>{txt}</Text> } itemSelected(item: Item){ console.log('the current item is===='+JSON.stringify(item)); const result = item.countryCode + ' ' + item.phoneCode; if(this.props.updatePhoneCode){ this.props.updatePhoneCode(result, item.phoneCode); this.props.navigator.pop(); } } onSideBarSelected(letter:string){ let sections: sectionData[] = this.state.totalSections; if (sections) { for (let index = 0; index < sections.length; index++) { let item = sections[index]; if (item.key === letter) { console.log('the current index i ==' + index); this.sectionList.scrollToLocation({animated: true, itemIndex: 0, sectionIndex: index, viewPosition: 0.0}); break; } } } }
好友列表
好友列表,好友列表的效果和上面一樣,也是需要分組和索引,由於好友列表開發的比較早,我是採用的FlatList
實現的功能。
程式碼如下:
renderSuccessView(){ return ( <View style={styles.container}> <View style={styles.content}> <FlatList ref={el=> this.list = el} data={this.state.listData} extraData={this.state} keyExtractor={(item: any, index: number)=> index.toString()} renderItem={this._renderItem} getItemLayout={this._getItemLayout} ListEmptyComponent={this.renderEmptyView} ItemSeparatorComponent={this.itemSeparator} initialNumToRender={20} //onEndReachedThreshold={0.8} //legacyImplementation={this.state.listData.length > 0} showsVerticalScrollIndicator={true} /> <View style={{position: 'absolute', height: '100%', width: 15, right: 0, backgroundColor: 'transparent', justifyContent: 'center', alignItems: 'center'}}><SideBar indexs={this.indexs} onLetterSelectedListener={this.onSideBarSelected.bind(this)}/></View> </View> <View style={styles.divider}></View> </View> ); } _renderItem = (item:any) => { console.log('the current item-->' + JSON.stringify(item)+" key: "+ item.item.key); return (<FriendListItem item={item.item} spreadValue={this.state.scaleValue} onListItemClick={this.onListItemClick.bind(this)} acceptNewFriend={this.acceptNewFriend.bind(this)} newFriendsSectionUnfold={this.newFriendsSectionUnfold.bind(this)}> </FriendListItem>); }
好友列表剛開始做的時候bug比較多,經過幾次修改,修復了出現的那些bug,效果也打到達了策劃的要求。本以為任務完成了,還算完美。但是在接下來的反覆測試中,靈異事件出現了。
靈異事件
在測試的過程中,出現了好友列表只加載10條的情況,10是initialNumToRender
設定第一屏渲染的資料條數。而且很奇怪,好友列表第一個固定的分組是好友的申請記錄,其餘的顯示已經是好友的資料,按拼音的首字母進行分組排序。好友申請記錄的顯示邏輯是一開始最多顯示5條,點選展開按鈕,在全部顯示出來。
好友列表的靈異事件剛開始發現的時候,好友申請記錄顯示了5條,已經是好友的資料顯示了5條,當點選了申請記錄的展開按鈕,申請記錄是6條,已經是好友的資料就少了最後一條,變成了4條,但是把申請收齊,好友資料又成了5條了。
剛開始以為是之前的bug沒有改徹底,之前因為因為有key重複的情況,出現了資料缺少的情況,經過反覆review程式碼,發現不是那個bug。反覆進入好友列表,發現只渲染10條的靈異事件,低端機上比較容易出現,比如iphone5S,Android的低端機上也比較容易出現,高階機上出現的頻率很低。
於是去測試國家編碼列表,那個列表一共是242條資料,也是右邊有分組索引,也出現了同樣的問題。
而且出現的時候列表的可滑動區域變得很大,列印資料來源,發現數據源是正常的,只是螢幕上只渲染了initialNumToRender設定的條數,就算你設定了2,也只顯示2條,設定一個很大的值,也能渲染,就是渲染慢,要等待,資料條數越多,時間越長。
網上查了很多資料,嘗試了很多解決辦法。
- 1.分頁載入資料。
- 2.列表的Item繼承PureComponent。
- 3.使用ListView替代。
分頁載入
分頁載入是網上查詢到的,說是FlatList
& SectionList
提供了分頁載入的觸發方法,當一屏資料快要滑動到底部的時候,觸發一個方法,可以再去載入另外一部分資料。於是乎趕緊實現了一把,資料也是分頁載入的,經過反覆測試,發現分頁載入的情況下,也會出現不再渲染的情況。而且因為列表右邊有索引,分頁載入其實也不能滿足需求,本想著分頁載入能夠解決靈異事件,去和策劃溝通一下,索引的功能稍微改一改,結果發現分頁載入的思路也行不通。
列表Item繼承PureComponent
網上查到一份資料,有人說是把列表的item的直接繼承PureComponent
(之前繼承的是React.Component
),於是滿懷希望趕緊把item重新改寫了一下,最後發現然並卵,那個靈異效果依舊會出現。
使用ListView替代
使用ListView
替代,查了一下關芳芳文件,說是ListView
本身的效能不好,才重新封裝了FlatList
& SectionList
來替代它。用ListView
替代以後,列表能夠完全渲染了,就是有點慢,而且官方文件說ListView
在超出螢幕之外,並沒有做回收,資料量打了,終究是個隱患。記憶體暴漲,滑動卡頓。
其實在測試的過程中,發現如果直接把 列表的initialNumToRender設定成列表資料的長度,也能暫時解決問題,但是資料量大了,載入會很慢。拿國家編碼列表來舉例,242條資料,大概需要卡住5s左右,隨著資料量的增大,效果可想而知,好友列表的資料容納量目標是1500條,瞬間就淚崩了。
總結
目前來說,經過網上查詢資料,自己嘗試,都木有徹底解決React-Native列表的靈異問題,目前的解決方案,只是暫時性,資料量大了,問題會很明顯,希望遇到過同樣問題的同學,能夠一起討論解決,共同成長,或者有解決方案,麻煩告知一聲,謝謝了。