React-Native iOS 列表(ListView)優(yōu)化方案

在項(xiàng)目開(kāi)發(fā)中,很多地方用到了列表,而 React-Native 官網(wǎng)中提供的組件 ListView,雖然能夠滿足我們的需求,但是性能問(wèn)題并沒(méi)有很好的解決,對(duì)于需要展現(xiàn)大量數(shù)據(jù)的列表,app 的內(nèi)存將會(huì)非常龐大。針對(duì) React-Native 的列表性能問(wèn)題,現(xiàn)在提供幾套可行性方案:

1.利用 Facebook 提供的建議對(duì) ListView 進(jìn)行優(yōu)化

Facebook 官方對(duì) ListView 的性能優(yōu)化做了簡(jiǎn)單介紹,并提供了以下幾個(gè)方法:

  • initialListSize
  • 這個(gè)屬性用來(lái)指定我們第一次渲染時(shí),要讀取的行數(shù)。如果我們想盡可能的快,我們可以設(shè)置它為1, 然后可以在后續(xù)的幀中,填棄其它的行。每一次讀取的行數(shù),由 pageSize 決定.
  • pageSize
  • 在使用了 initialListSize 之后,ListView 根據(jù) pageSize 來(lái)決定每一幀讀取的行數(shù),默認(rèn)值為1, 但如果你的的 views 非常的小,并且讀取時(shí)占的資源很少, 你可以調(diào)整這個(gè)值,在找到適合你的值。
  • scrollRenderAheadDistance
  • 如果我們的列表有2000個(gè)項(xiàng),而讓它一次性讀取,它會(huì)導(dǎo)致內(nèi)存和計(jì)算資源的耗盡。所以 scrollRenderAhead distance 可以指定,超出當(dāng)前視圖多少,繼續(xù)宣染。
  • removeClippedSubviews
  • “當(dāng)它設(shè)置為true時(shí),當(dāng)本地端的superview為offscreen時(shí) ,不在屏幕上顯示的子視圖offscreen(它的overflow的值為hidden) 會(huì)被刪除。它可以改善長(zhǎng)列表的滾動(dòng)的性能,默認(rèn)值為true.
    這對(duì)于大的ListViews來(lái)說(shuō)是一個(gè)非常重要。在Android, overflow的值通常為hidden. 所以我們并不需要擔(dān)心它的設(shè)置,但是對(duì)于iOS來(lái)說(shuō),你需要設(shè)置row Container的樣式為overflow: hidden。

在使用了上述方法后,我們可以看到app的內(nèi)存占用有了一定的下降(加載100張圖片時(shí)的效果):

使用前

MacDown Screenshot

使用后
MacDown Screenshot

3.橋接 Native tableview

第二種方法里面,能夠比較好的解決屏幕外的 cell 內(nèi)存問(wèn)題,但是和 native tableview 相比,并沒(méi)有 native 的 cell 重用機(jī)制完美,那么,我們可以講 native 的 tableview 橋接到 React-native 中來(lái),讓我們可以在 React-Native 中也可以重用 cell

我們創(chuàng)建一些 VirtualView,他只是遵從了 RCTComponent 協(xié)議,其實(shí)并不是一個(gè)真正的 View,我把它形成一個(gè)組件,把它 Bridge 到 JS,這就使得,你在寫(xiě) JSX 的時(shí)候,就可以直接用 VirtualView 來(lái)去做布局了。在RN里面做布局的時(shí)候我們用VirtualView來(lái)做布局。但是最終在 insertReactSubview 時(shí),我們把這些 VirtualView 當(dāng)做數(shù)據(jù)去處理,通過(guò) VirtualView 和RealView 的對(duì)應(yīng)關(guān)系,把它轉(zhuǎn)化成一個(gè)真實(shí)的 View 對(duì)象添加到 TableView 中去。

但是使用這種方法,我們需要將 tableview 的所有常用數(shù)據(jù)源方法和代理方法都橋接到 React-Native 中來(lái),甚至對(duì)于一些 cell 組件,我們也需要自己橋接,并不能像 React-Native 那樣使用自己的組件。當(dāng)我們的需求比較復(fù)雜或者需求發(fā)生變化時(shí),就需要重新橋接我們的自定義 cell,這樣工作量就會(huì)比較大。大多數(shù)的 cell 里面如果做展示來(lái)用的話,Label 和 Image 基本上能夠滿足大多數(shù)的需求了。所以我們現(xiàn)在只是做了 Label 和 Image 的對(duì)應(yīng)工作,但在 RN 的一些官方控件,在這個(gè) view 里面都是沒(méi)法直接使用的。

4.用 JS 實(shí)現(xiàn)一套 cell 重用的邏輯

基于 RN 的 ScrollView,我們也監(jiān)聽(tīng) OnScroll(),他往上滑的時(shí)候,我們需要把上面的 cellComponent 挪下來(lái),挪到上面去用。但是這個(gè)方式最終的效果并不是特別好。

問(wèn)題在于,如果我們所有的 Cell 都是一樣高的,里面的元素不是很多的情況下,性能還相對(duì)好一些,我們每次 OnScroll 的時(shí)候,他處理的Cell比較少。如果你希望有一個(gè)界面滾動(dòng)能夠達(dá)到流暢的話,所有的處理都需要在 16ms 內(nèi)完成,但是這又造成了 onScroll 都要去刷新頁(yè)面,導(dǎo)致這樣的交互會(huì)非常非常多,導(dǎo)致你從 JS,到 native 的 bridge 要頻繁的通訊,JS 中的很多處理方式都是異步的,使得這個(gè)方案的效果沒(méi)有達(dá)到很好的預(yù)期。

總結(jié)

從上面的幾種方案可以看出,方案1、2、3、4都能夠比較好的解決列表的性能問(wèn)題 ,而且各有優(yōu)缺點(diǎn),那么,我們?cè)陧?xiàng)目開(kāi)發(fā)中該如何應(yīng)用呢?
  • 當(dāng)我們?cè)谶M(jìn)行列表展示的時(shí)候,如果數(shù)據(jù)量不是特別的龐大(不是無(wú)限滾動(dòng)的),且界面比較復(fù)雜的時(shí)候,方案1能夠比較好的解決性能問(wèn)題,而且操作起來(lái)比較簡(jiǎn)單,只需要對(duì) listview 的一些屬性進(jìn)行基本設(shè)置。

  • 當(dāng)我們需要展示很多數(shù)據(jù)的時(shí)候(不是無(wú)限滾動(dòng)的),我們可以使用方案2,對(duì)那些超出屏幕外的部分,對(duì)他進(jìn)行組件最小化

  • 當(dāng)我們需要展示大量數(shù)據(jù)(可以無(wú)限滾動(dòng)的),我們可以通過(guò)方案3/4,來(lái)達(dá)到重用的目的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容