跳到主要內容

React Native 中更優良的列表視圖

·6 分鐘閱讀
Spencer Ahrens
Facebook 軟體工程師

在我們於社群群組中發布搶先看公告後,你們許多人已經開始試用我們的一些全新列表組件,但我們今天正式宣布它們!不再有 ListViewDataSource、過時的列、被忽略的錯誤或過多的記憶體消耗 - 透過最新的 React Native 2017 年 3 月候選版本 (0.43-rc.1),您可以從新的組件套件中挑選最適合您用例的組件,它們開箱即用,具有出色的效能和功能集

<FlatList>

這是用於簡單、高效能列表的工作馬組件。提供資料陣列和 renderItem 函式,您就可以開始使用了

<FlatList
data={[{title: 'Title Text', key: 'item1'}, ...]}
renderItem={({item}) => <ListItem title={item.title} />}
/>

<SectionList>

如果您想要呈現一組分成邏輯區段的資料,可能包含區段標題 (例如,在字母順序的通訊錄中),並且可能具有異質資料和呈現方式 (例如,包含一些按鈕、後接撰寫器、然後是照片網格、然後是朋友網格,最後是故事列表的個人資料檢視),這是最佳選擇。

<SectionList
renderItem={({item}) => <ListItem title={item.title} />}
renderSectionHeader={({section}) => <H1 title={section.key} />}
sections={[ // homogeneous rendering between sections
{data: [...], key: ...},
{data: [...], key: ...},
{data: [...], key: ...},
]}
/>

<SectionList
sections={[ // heterogeneous rendering between sections
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
]}
/>

<VirtualizedList>

幕後實作具有更彈性的 API。如果您的資料不是純陣列 (例如,不可變列表),則特別方便。

功能

列表在許多情境中使用,因此我們在新組件中加入了完整的功能,以處理大多數開箱即用的用例

  • 滾動載入 (onEndReached)。
  • 下拉重新整理 (onRefresh / refreshing)。
  • 可配置的可見性 (VPV) 回呼 (onViewableItemsChanged / viewabilityConfig)。
  • 水平模式 (horizontal)。
  • 智慧型項目和區段分隔符。
  • 多欄支援 (numColumns)
  • scrollToEndscrollToIndexscrollToItem
  • 更佳的 Flow 型別。

一些注意事項

  • 當內容滾動出渲染視窗時,項目子樹的內部狀態不會保留。請確保您的所有資料都擷取在項目資料或外部儲存區中,例如 Flux、Redux 或 Relay。

  • 這些組件基於 PureComponent,這表示如果 props 保持淺層相等,它們將不會重新渲染。請確保您的 renderItem 函式直接依賴的所有內容都作為 prop 傳遞,該 prop 在更新後不是 ===,否則您的 UI 可能不會在變更時更新。這包括 data prop 和父組件狀態。例如

    <FlatList
    data={this.state.data}
    renderItem={({item}) => (
    <MyItem
    item={item}
    onPress={() =>
    this.setState(oldState => ({
    selected: {
    // New instance breaks `===`
    ...oldState.selected, // copy old data
    [item.key]: !oldState.selected[item.key], // toggle
    },
    }))
    }
    selected={
    !!this.state.selected[item.key] // renderItem depends on state
    }
    />
    )}
    selected={
    // Can be any prop that doesn't collide with existing props
    this.state.selected // A change to selected should re-render FlatList
    }
    />
  • 為了限制記憶體並實現流暢滾動,內容會異步地在螢幕外渲染。這表示滾動速度可能快於填充率,並且可能會短暫看到空白內容。這是一種權衡,可以調整以滿足每個應用程式的需求,我們正在幕後努力改進它。

  • 預設情況下,這些新列表會在每個項目上尋找 key prop,並將其用於 React 金鑰。或者,您可以提供自訂的 keyExtractor prop。

效能

除了簡化 API 之外,新的列表組件還具有顯著的效能增強功能,主要功能是幾乎恆定的記憶體使用量,適用於任何數量的列。這是透過「虛擬化」渲染視窗外部的元素來完成的,方法是將它們從組件層次結構中完全卸載,並從 react 組件回收 JS 記憶體,以及從陰影樹和 UI 檢視回收原生記憶體。這有一個缺點,即內部組件狀態將不會保留,因此請確保您在組件本身外部追蹤任何重要狀態,例如在 Relay 或 Redux 或 Flux 儲存區中。

限制渲染視窗也減少了 React 和原生平台需要完成的工作量,例如來自檢視遍歷。即使您要渲染一百萬個元素的最後一個,使用這些新列表也不需要迭代所有這些元素才能渲染。您甚至可以使用 scrollToIndex 跳到中間,而無需過度渲染。

我們還對排程進行了一些改進,這應有助於應用程式的響應能力。渲染視窗邊緣的項目會不頻繁地渲染,並在任何活動手勢或動畫或其他互動完成後以較低的優先級渲染。

進階用法

ListView 不同,渲染視窗中的所有項目會在任何 prop 變更時重新渲染。通常這沒問題,因為視窗化將項目數量減少到恆定數量,但如果您的項目屬於複雜類型,您應確保遵循 React 效能最佳實務,並在您的組件中使用 React.PureComponent 和/或 shouldComponentUpdate 以限制遞迴子樹的重新渲染。

如果您可以在不渲染列的情況下計算列的高度,則可以透過提供 getItemLayout prop 來改善使用者體驗。這使得使用例如 scrollToIndex 滾動到特定項目時更加流暢,並且將改善滾動指示器 UI,因為可以在不渲染內容的情況下確定內容的高度。

如果您有替代資料類型,例如不可變列表,<VirtualizedList> 是最佳選擇。它採用 getItem prop,可讓您傳回任何給定索引的項目資料,並具有較寬鬆的 flow 型別。

如果您有不尋常的用例,還可以調整許多參數。例如,您可以使用 windowSize 來權衡記憶體使用量與使用者體驗、maxToRenderPerBatch 來調整填充率與響應能力、onEndReachedThreshold 來控制滾動載入何時發生等等。

未來工作

  • 遷移現有表面 (最終棄用 ListView)。
  • 當我們看到/聽到需求時,會提供更多功能 (請告訴我們!)。
  • 固定區段標題支援。
  • 更多效能最佳化。
  • 支援具有狀態的功能項目組件。