優化 React 前端性能的五個技巧

作者|Piumi Liyana Gunawardhana

譯者 / 策劃|張衛濱

在現代 Web 應用開發中,性能是獲得更好的用戶體驗的關鍵。使用 React 能夠產生直觀的用戶界面,爲衆多應用提供更強大的用戶體驗,而且無需花費很多的精力來顯式地優化性能。但是,開發人員在構建大型應用時,往往受困於性能問題,我們可以採用各種方法來進一步優化 React 應用的性能。

在本文中,我將討論提升 React 前端性能的五個技巧。

  1. 使用 React.PureComponent 

避免進行協調

React 使用了虛擬 DOM(VDOM)的概念,在這種概念中,像 ReactDOM 這樣的庫會在內存中維護用戶界面的 “虛擬” 表述,並使其與 “實際” 的 DOM 保持同步。這個過程叫做協調(reconciliation)。

即便只更新已修改的 DOM 節點,重新渲染也會需要時間。生命週期函數 shouldComponentUpdate() 會在重新渲染過程開始之前被調用,這通常都不是什麼問題,但是,如果延遲很明顯的話,我們可以通過覆蓋 shouldComponentUpdate() 來加速這一過程。該函數的默認實現會返回 true,即讓 React 來處理更新,如下所示:

shouldComponentUpdate(nextProps, nextState) {
  return true;
}

如果你知道自己的組件在某些情況下不需要更新的話,那麼可以從 shouldComponentUpdate() 中返回 false 來完全省略渲染過程。

在 React“純” 組件的幫助下,這種優化策略可以變得更加容易、更加自動化。PureComponent 使用淺層(shallow)屬性和狀態對比來實現其 shouldComponentUpdate()。

這意味着它會對原始數據類型的值和對象的引用進行對比。因此,在使用 React.PureComponent 時,我們需要滿足如下兩個先決條件:

  1. 使用不可變的數據結構

假設我們要思考一種有效的方式來自動探測複雜屬性或狀態變更,這裏就是不可變數據結構的用武之地了。

使用不可變數據結構背後的理念很簡單:與其直接對包含複雜數據的對象進行修改,不如創建一個包含數據變更的副本。因此,識別對象變化就可以像比較兩個對象的引用一樣容易。

爲了自動檢查複雜的狀態變更,可以組合使用 React.PureComponent 和不可變數據結構。例如,藉助像 Redux 這樣的狀態管理工具,如果應用中的狀態是不可變的,我們可以將所有的狀態對象保存到一個單獨的存儲中,這樣就可以很容易地實現撤銷和重做功能。

如果能夠使用提供不可變數據結構的庫會更好。在處理高度嵌套的對象時,更新不可變的對象可能是一項挑戰。如果你遇到這種問題,可以考慮使用 Immer 或 immutability-helper。這些庫能夠讓我們創建更加易讀的代碼,同時保留了不可變性的優勢。

使用不可變數據結構會帶來一些收益:

  1. 使用生產化的構建

當創建 React 應用時,你會看到很多有用的警告和錯誤信息。在開發軟件的過程中,它們能夠讓發現缺陷和問題變得更加容易。但是,它們在性能方面是有代價的。

因此,如果你要做基準測試或者遇到性能問題,請使用最小化的生產化構建來測試你的 React 應用。終端用戶不需要 React 在開發環境中運行的這些代碼片段。這些不相關的代碼都可以在生產環境中移除。

如果你使用 create-react-app 進行的項目初始化的話,那麼可以使用 npm run build 來生成沒有這些額外代碼的生產化構建。如果你直接使用 Webpack 的話,那麼可以運行 webpack -p(相當於 webpack — optimize-minimize — define process.env.NODE ENV=”’production’”)。

在你項目的 /build 子目錄下,現在將會包含應用的生產化構建。請記住,這僅在生產化部署時纔是必要的,在通常的開發中,依然可以使用 npm start。

  1. 多個分塊文件

在項目初期,你的 React 應用可能只有幾個組件。但是,隨着不斷添加新的功能和依賴,應用也會隨着時間的推移而不斷增長。不知不覺中,你可能就會有一個非常龐大的生產化文件。

對於單頁的 React 應用,我們通常會將所有的前端 JavaScript 代碼打包到一個最小化的文件中。對於中小規模的應用來說,這是合理的。但是,當應用的規模擴大時,將這些打包的 JavaScript 代碼發送到瀏覽器會耗費大量的時間,從而影響應用的性能。

如果你使用 Webpack 來構建 React 項目的話,那麼可以使用它的代碼拆分功能,也就是將要構建的應用代碼分割成多個 “塊(chunk)”。例如,我們可以使用 CommonsChunkPlugin 將應用打包成兩個獨立的文件,也就是將供應商或第三方庫的代碼與我們的應用代碼區分開,並按需傳遞給瀏覽器。這樣的話,我們最後會得到名爲 vendor.bundle.js 和 app.bundle.js 的兩個文件。通過拆分文件,我們的瀏覽器能夠減少緩存並且能夠並行加載資源,以儘量減少加載時間的延遲。

按需拆分代碼是 Webpack 的一個優秀特性。它可以將代碼分割成較小的塊,這些塊可以在需要時加載。這實現了最小的初始下載量,減少了應用的加載時間。然後,瀏覽器可以根據應用的需要下載更多的代碼塊。

  1. 虛擬化長列表

如果你正在展示一個包含大量數據的長列表,那麼在給定的時間,你應該僅在可見的視口(viewport)內渲染一部分的數據。數據僅在視口中顯示,當列表向下滾動時,更多的數據會被渲染出來。這種技術被稱爲 “窗口化(windowing)”。該技術可以大大減少重新渲染組件所消耗的時間以及所生成的 DOM 節點的數量,因爲每次它僅渲染很小的一部分數據行。

知名的窗口化庫是 react-window 和 react-virtualized。它們提供了幾個可重用的組件來展示數據表格、列表和表格化的數據。當然,如果你需要滿足應用特定的需求,那麼可以像 Twitter 那樣不斷地開發自己的窗口組件。

最後的思考

React Web 應用的性能是由其組件的簡潔性決定的。因此,在解決性能問題之前,瞭解 React 組件的工作方式、渲染和 diffing 技術是至關重要的。在 React 生命週期方法的幫助下,我們可以避免不必要的組件渲染。如果消除了這些限制,應用能夠像用戶期望的那樣流暢運行。所以,在增強 React 應用的性能時,你應該考慮所有的這些想法。

儘管還有其他各種方法來增強 React 應用的性能,但我希望這篇文章能幫助你獲得優化 React 前端性能的最佳技巧。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/wmRnXOhGOy4QVpGj5oaABg