1. 程式人生 > >React-Native開發中的靈異元件-列表

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列表的靈異問題,目前的解決方案,只是暫時性,資料量大了,問題會很明顯,希望遇到過同樣問題的同學,能夠一起討論解決,共同成長,或者有解決方案,麻煩告知一聲,謝謝了。