看Facebook是如何優化React Native性能

原文出處: facebook   譯文出處:@Siva海浪高   

該文章翻譯自Facebook官方博客,傳送門

React Native 允許我們運用 React 和 Relay 提供的聲明式的編程模型,寫JavaScript來構建我們的 iOS 和 Android 的應用。這樣的做法使得我們的代碼更精簡,更容易理解和閱讀,這些代碼還可以在多個平臺共享。我們也可以加快迭代速度(因爲在開發時不用等待漫長的編譯。使用React Native,我們可以發佈更快,打磨更多細節,讓應用運行的更流暢。這其中優化性能是我們工作的一大重要部分,接下來講述 Facebook 如何使應用性能足足提升兩倍的故事~

爲什麼要加快?

當應用運行的更快,內容加載的更迅速,就意味着用戶可以有更多時間來使用應用,流暢的動畫讓用戶更加享受的使用應用。在新型市場中,2G網絡和幾年前的機型還是主力。這時那些性能良好的和那些運行卡頓就有很大差別了。

自從發佈了 iOS 和 Android 版本的 React Native 後,我們團隊一直在諸如 提升列表視圖的滾動性能,優化內存佔有,讓 UI 界面更具響應性和加快應用啓動速度 上做了不少工作。這其中應用啓動關乎初次印象和是框架其他部分的壓力源頭,所以它是要解決的頭等難題。

量化一切

我們把Facebook的iOS版中的事件主頁用RN重新實現(在更多標籤頁下點擊事件進入查看)。這是個非常好的用於測試性能的例子,因爲原生版已經做了大量的優化工作,而且該頁面也是非常好的典型列表交互的例子。

接下來,我們自動化的 CT-Scan 性能測試來幫助我們自動定位到我們需要到的標籤頁。然後反覆打開和關閉事件主頁50次。在每次交互中,我們能夠記錄下從點擊事件按鈕到事件主頁能夠被完整顯示的時間,我們也添加更多詳細的性能埋點來告訴我們啓動過程哪些步驟是緩慢或消耗CPU的

下面是我們記錄和測量的一些步驟的大致描述:

1 原生啓動:初始化JavaScript虛擬機和其他一些原生模塊(如磁盤緩存,網絡,UI管理器等)

2 JS初始化和依賴加載:從手機存儲中讀取被壓縮的JS代碼,加載到JavaScript虛擬機,從而解析和產生字節碼,加載相關的依賴

3 取數據前:加載和執行事件主頁的應用代碼,構建Relay的查詢語句,然後觸發取數據。

4 取數據:從手機磁盤緩存讀取數據

5 JS渲染:初始化所有相關的React組件,把它們發送到原生的UI管理器模塊來顯示。

6 原生渲染:在shadow線程中先通過根據 FlexBox 佈局計算視圖大小。然後在主線程中創建和定位這些視圖。

我們根據於此的黃金法則是:永遠不要忘了迴歸測試。我們持續的運行它來追蹤性能提升和功能迴歸。開發者在提交改動的代碼之前用它對特定的提交做運行和詳細的性能分析。其他的一些測試也需要被同樣的方式建立來衡量諸如功能性能和內存使用等

啓動時發生了什麼

當我們設置好自動性能追蹤,我們需要一個工具來給我們更多細節來決定啓動過程中的那些部分需要優化。我們在我們框架裏添加詳細的啓動/暫停的性能錨點,收集數據,使用 catapult 查看器來定位熱點和阻塞線程間交互的。也可以從開發者菜單下觸發開始對我們應用的性能分析。

在RN中,代碼是在JavaScript線程中執行的。每次你要寫數據到磁盤,在一次網絡請求,或者取一些其他原生的資源(如攝像機),你的代碼都需要調用原生模塊。當你要渲染力你的 React 組件,它們會被轉發到界面管理器的原生模塊中,它在主線程中來執行佈局和創建相應的視圖。橋協議來轉發請求到原生模塊和被回調到你的JS代碼(如果需要)。在RN中,所有原生的調用必須是異步的來避免阻塞主線程和JS線程。

在下面的事件組件的啓動可視化圖中,我們可以看到應用在 JS 隊列中運行,爲了顯示事件列表,觸發了相關的緩存讀取(在本地存儲隊列中被異步觸發)。一旦它取得了緩存數據,應用在 JS 隊列用 React 渲染事件單元格,接着又傳給柵格隊列來佈局和最終傳給主隊列來創建視圖顯示。這個例子展示了多個緩存讀取(組合成單個常用讀取操作可以做到更快)和一些React在JS線程上的渲染操作可以被統一合併。

性能提升

下面是那些我們在實施過程中最重要的性能和時序安排的提升做法,同時配有對應代碼提交的鏈接。

啓動時少做些

安排合適時機執行

  • 懶加載

  • Relay的增量緩存讀取:Relay一開始是web項目而生所以僅僅把請求響應放在內存中 – 要從磁盤讀取的第一個請求的緩存響應需要從磁盤中讀取全部的緩存到內存中。通過只讀取滿足特定查詢請求的緩存,我們可以顯著減少 I/O 負載和原生到JS橋的流量。

  • 不用批量橋協議調用,要批量Relay調用:一開始我們認爲通過把JS請求批量發送給原生模塊可以減少調用原生到JS橋的負載,但是性能分析告訴我們JS和原生間的橋調用根本不是性能瓶頸。事實上,UI界面或緩存讀取的批量操作的延遲也會延遲原生線程的操作,從而影響應用性能。在其他case上,注入Relay用於拉取多個鍵值數據的緩存讀取,通過批處理可以有顯著提升。

  • 更早的界面填充

  • 懶加載原生模塊

  • 對文本組件的觸摸做懶綁定:綁定觸摸事件回調會需要不少時間。所以我們現在僅僅先綁定觸摸開始事件touch down event(就是當你第一次觸摸對象時)然後只當你觸摸對象後纔開始綁定其他回調函數,而不是一開始就全部綁定對調

  • 延遲流行事件的查詢:

爲光速做準備

幾個月前,事件主頁的啓動在 iPhone5 上需要2秒。經過我們在RN上的大量性能優化工作,在倫敦,門洛帕克和紐約的RN,React和Relay團隊,事件主頁的啓動被加快了一倍。而且大部分我們實施的優化是在RN的框架層的,這就意味着開發者們的RN應用也會自動得益於這些工作(當他們把應用遷移到最新版本的RN下

這些優化才僅僅是個開始:我們會繼續在整個棧的各個部分都開展工作,從JavaScript代碼解析時間到數據拉取性能。同時,你們也可以給社區貢獻,學習如何讓應用更快,在社區論壇提出你可能遇到的任何問題。QQ技術交流羣290551701

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章