本文是【React Native 性能優化指南】的一部分內容,因為內容比較具有代表性,所以單獨拿出進行講解;若想獲得完整優化建議,可點擊原文查看,
在 React Native 開發中,最容易遇到的對性能有一定要求場景就是長串列了,在日常業務實踐中,優化做好后,千條資料渲染還是沒啥問題的,
虛擬串列前端一直是個經典的話題,核心思想也很簡單:只渲染當前展示和即將展示的 View,距離遠的 View 用空白 View 展示,從而減少長串列的記憶體占用,
在 React Native 官網上,?? 串列配置優化其實說的很好了,我們基本上只要了解清楚幾個配置項,然后靈活配置就好,但是問題就出在「了解清楚」這四個字上,本節我會結合圖文,給大家講述清楚這幾個配置,
1?? 各種串列間的關系
React Native 有好幾個串列組件,先簡單介紹一下:
- ScrollView:會把視圖里的所有 View 渲染,直接對接 Native 的滾動串列
- VirtualizedList:虛擬串列核心檔案,使用 ScrollView,長串列優化配置項主要是控制它
- FlatList:使用 VirtualizedList,實作了一行多列的功能,大部分功能都是 VirtualizedList 提供的
- SectionList:使用 VirtualizedList,底層使用 VirtualizedSectionList,把二維資料轉為一維資料
還有一些其他依賴檔案,有個?? 博文的圖總結的挺好的,我這里借用它的圖一下:

我們可以看出 VirtualizedList 才是主演,下面我們結合一些示例代碼,分析它的配置項,
2?? 串列配置項
講之前先寫個小 demo,demo 非常簡單,一個基于 FlatList 的奇偶行顏色不同的串列,
export default class App extends React.Component {
renderItem = item => {
return (
<Text
style={{
backgroundColor: item.index % 2 === 0 ? 'green' : 'blue',
}}>
{'第 ' + (item.index + 1) + ' 個'}
</Text>
);
}
render() {
let data = https://www.cnblogs.com/skychx/p/[];
for (let i = 0; i < 1000; i++) {
data.push({key: i});
}
return (
VirtualizedList 有個 debug 的配置項,開啟后會在視圖右側顯示虛擬串列的顯示情況,
這個屬性檔案中沒有說,是翻?? 原始碼發現的,我發現開啟它后用來演示講解還是很方便的,可以很直觀的學習 initialNumToRender、windowSize、Viewport,Blank areas 等概念,
下面是開啟 debug 后的 demo 截屏:

上面的圖還是很清晰的,右側 debug 指示條的黃色部分表示記憶體中 Item,各個屬性我們再用文字描述一下:
1.initialNumToRender
首批應該渲染的元素數量,剛剛蓋住首屏最好,而且從 debug 指示條可以看出,這批元素會一直存在于記憶體中,
2.Viewport
視口高度,就是用戶能看到內容,一般就是設備高度,
3.windowSize
渲染區域高度,一般為 Viewport 的整數倍,這里我設定為 3,從 debug 指示條可以看出,它的高度是 Viewport 的 3 倍,上面擴展 1 個螢屏高度,下面擴展 1 個螢屏高度,在這個區域里的內容都會保存在記憶體里,
將 windowSize 設定為一個較小值,能有減小記憶體消耗并提高性能,但是快速滾動串列時,遇到未渲染的內容的幾率會增大,會看到占位的白色 View,大家可以把 windowSize 設為 1 測驗一下,100% 會看到占位 View,
4.Blank areas
空白 View,VirtualizedList 會把渲染區域外的 Item 替換為一個空白 View,用來減少長串列的記憶體占用,頂部和底部都可以有,
上圖是渲染圖,我們可以利用 react-devtools 再看看 React 的 Virtual DOM(為了截屏方便,我把 initialNumToRender 和 windowSize 設為 1),可以看出和上面的示意圖是一致的,

5.removeClippedSubviews
這個翻譯過來叫「裁剪子視圖」的屬性,檔案描述不是很清晰,大意是設為 true 可以提高渲染速度,但是 iOS 上可能會出現 bug,這個屬性 VirtualizedList 沒有做任何優化,是直接透傳給 ScrollView 的,
在 0.59 版本的一次 ?? commit 里,FlatList 默認 Android 開啟此功能,如果你的版本低于 0.59,可以用以下方式開啟:
removeClippedSubviews={Platform.OS === 'android'}
6.maxToRenderPerBatch 和 updateCellsBatchingPeriod
VirtualizedList 的資料不是一下子全部渲染的,而是分批次渲染的,這兩個屬性就是控制增量渲染的,
這兩個屬性一般是配合著用的,maxToRenderPerBatch 表示每次增量渲染的最大數量,updateCellsBatchingPeriod 表示每次增量渲染的時間間隔,
我們可以調節這兩個引數來平衡渲染速度和回應速度,但是,調參作為一門玄學,很難得出一個統一的「最佳實踐」,所以我們在業務中也沒有動過這兩個屬性,直接用的系統默認值,
2?? ListLtems 優化
?? ListLtems 優化 檔案:https://reactnative.cn/docs/optimizing-flatlist-configuration/#list-items
檔案中說了好幾點優化,其實在前文我都介紹過了,這里再簡單提一下:
1.使用 getItemLayout
如果 FlatList(VirtualizedList)的 ListLtem 高度是固定的,那么使用 getItemLayout 就非常的合算,
在原始碼中(#L1287、#L2046),如果不使用 getItemLayout,那么所有的 Cell 的高度,都要呼叫 View 的 onLayout 動態計算高度,這個運算是需要消耗時間的;如果我們使用了 getItemLayout,VirtualizedList 就直接知道了 Cell 的高度和偏移量,省去了計算,節省了這部分的開銷,
在這里我還想提一下幾個注意點,希望大家使用 getItemLayout 要多注意一下:
- 如果 ListItem 高度不固定,使用 getItemLayout 回傳固定高度時,因為最終渲染高度和預測高度不一致,會出現頁面跳動的問題【?? 問題鏈接】
- 如果使用了
ItemSeparatorComponent,分隔線的尺寸也要考慮到 offset 的計算中【?? 檔案鏈接】 - 如果 FlatList 使用的時候使用了
ListHeaderComponent,也要把 Header 的尺寸考慮到 offset 的計算中【?? 官方示例代碼鏈接】
2.Use simple components & Use light components
使用簡單組件,核心就是減少邏輯判斷和嵌套,優化方式可以參考「二、減輕渲染壓力」的內容,
3.Use shouldComponentUpdate
參考「一、re-render」的內容,
4.Use cached optimized images
參考「三、圖片優化那些事」的內容,
5.Use keyExtractor or key
常規優化點了,可以看 React 的檔案 ?? 串列 & Key,
6.Avoid anonymous function on renderItem
renderItem 避免使用匿名函式,參考「四、物件創建呼叫分離」的內容,
最后推薦一下我的個人公眾號:「鹵蛋實驗室」,平時會分享一些前端技術和資料分析的內容,大家感興趣的話可以關注一波:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/87023.html
標籤:JavaScript
