React 組件渲染性能探索

原文地址:Component Rendering Performance in Reactjavascript

文章同步於Github Pines-Cheng/blogcss

React 由於性能好而被廣爲周知,但這並不意味着咱們可以把這個看成是理所固然。讓你的React應用更快的關鍵Tips之一就是優化你的 render 函數.html

我曾經建立過一個簡單的測試,來比較下面不一樣條件下的 render() 函數的速度:java

  • 無狀態(函數)組件(stateless components) vs 有狀態(基於class的)組件components
  • 純組件(Pure component)渲染 vs 無狀態組件
  • React 0.14 vs React 15 在development模式和production模式下的渲染性能

關鍵結論(TL;DR)

對於那些不耐煩,只是想閱讀結果的人來講,這是如下測試中得出的最重要的結論:react

  • 無狀態組件Stateless (functional) components 並不比有狀態組件 stateful (class) components
  • React 15 的渲染性能大概比 0.14 快 25%
  • 純組件 Pure components 更快,由於使用shouldComponentUpdate
  • development 模式下的渲染性能比 production 模式下慢 2–8x倍
  • React 15 development 模式下的渲染性能比 0.14 慢 2x 倍

很驚訝?webpack

與每一個基準同樣,弄懂結果的關鍵是先理解方法。讓咱們花一些時間來解釋我在這裏作了什麼,爲何要這麼作。git

怎樣進行性能測試

目標是建立一個很是簡單的測試來迭代 render() 函數。我建立了包含三種組件之一的父組件:github

  • 一個無狀態組件
  • 一個有狀態組件
  • 一個純粹的有狀態組件,即 shouldComponentUpdate 返回 false
// Stateful component
class Stateful extends Component {
  render () {
    return <div>Hello Cmp1: stateful</div>;
  }
}
// Pure stateful with disabled updates
class Pure extends Component {
  shouldComponentUpdate() {
    return false;
  }
  render () {
    return <div>Hello Cmp2: stateful, pure, no updates</div>;
  }
}
// Stateless component
function Stateless() {
  return <div>Hello Cmp3: stateless</div>;
}

測試的組件很簡單,並無改變DOM。web

頂層的組件將三種類型的組件每種循環渲染100,000次。同時使用瀏覽器自帶的 Performance.now功能,統計從初始渲染到最後渲染的時間來衡量渲染時間。遺憾的是,咱們不能使用React官方提供的的Perf函數來統計,由於它們不能用在生產環境中。算法

雖然始終傳遞props以確保更新,但目標組件保持渲染的數據相同。這樣咱們能夠保證與純組件的一致性。 DOM不會從這些組件中更新,從而保證其餘API(例如佈局呈現引擎)的干擾達到最小。

全部的JavaScript代碼都運行在純ES6中,沒有轉換步驟(沒有轉換爲ES5)

測試運行的瀏覽器爲Chrome 51 (包含經常使用的插件,如一系列的 dev tools, 1Password, Pocket等), 一個純粹的Chrome Canary 54 (沒有插件,沒有歷史記錄,徹底全新的) 和 Firefox 46. OS X 10.11,主機環境爲一臺2.6 GHz Intel Core i7 處理器的MBP.全部呈現的數據都是取全部瀏覽器運行結果的平均值。

TJ Holowaychuk’s 的觀點中,值得一提的是不安裝任何插件的瀏覽器可以產生一個明顯更好的結果。這就是咱們使用一個純粹的Chrome Canary配置的緣由。不管怎樣,咱們普通的用戶一般都會在瀏覽器安裝幾個插件,咱們並不知道這個會致使多大的性能損失。

執行這些類型的基準測試時的精確度並不容易實現。有時測試會運行速度更慢或更快,致使結果產生誤差。這就是爲何我屢次運行測試,並統計了全部的結果。您單次測試的結果可能會有所不一樣。

全部的 源碼 都在Github上,歡迎check out. 每一個框架對應一個文件夾,你可以在裏面運行常見的 npm i && npm start命令。這些應用運行在不一樣的端口,所以他們可以被同時執行。 readme 文件 提供了更詳盡的說明。

咱們已經有了這些前提,如今,讓咱們來討論一下這些發現。

神話:無狀態組件更快?

根據React 0.14和React 15,無狀態(functional)組件與常規的基於類的狀態組件同樣快。

image

您會注意到,React 0.14中的測試顯示無狀態組件與狀態組件性能有5%的差別,但我認爲這是統計偏差。

可是,當整個生命週期被剝離,且沒有引用,沒有狀態管理,無狀態組件爲什麼不會更快?

根據 Dan Abramov的說法,無狀態組件內部封裝在一個類中,當前並無進行任何的優化,

@ggrgur There is no 「optimized」 support for them yet because stateless component is wrapped in a class internally. It's same code path.

React團隊已經承諾將對於無狀態組件的優化,我確信在React的將來版本之一中將實現。

React中最簡單的性能技巧

拋開復雜構建優化問題,React應用程序性能調優的關鍵在於知道何時渲染,何時不渲染。

這裏有一個例子:假設您正在建立一個下拉菜單,並但願經過設置狀態來展開它expanded:true。這種狀況下,你必需要渲染整個塊,包括下拉列表中的全部列表項才能更改css值嗎?千萬不要這麼幹!

這種狀況下,shouldComponentUpdate 是你最好的朋友, 趕忙用上它。若是你可以使用shouldComponentUpdate進行優化,就不要使用無狀態組件。您可使用Shallow Compare函數來簡化流程,但若是這成爲React組件核心功能的一部分,我也不會感到驚訝。

更新:從React 15.3開始,React.PureComponent是一個新的基類,能夠替代使用Shallow Compare插件或Pure Render mixin的場景。

image

最初的基準測試是在沒有添加任何邏輯到render函數的狀況下,進行了渲染性能的比較。而一旦添加了計算,Pure Components的優點就會更加明顯。咱們能夠將其視爲一種緩存的方法。
我這麼直接緣由之一是看到開發人員不關心改善渲染,由於虛擬DOM將在這方面作了一些處理。可是顯然,在涉及到虛擬DOM的diff算法以前,應用的速度還有大量的提高空間。

我不想說你應該在全部的地方使用shouldComponentUpdate。添加大量的邏輯或在組件不多輸出相同的HTML代碼的地方使用它,就會增長沒必要要的複雜度。你應該知道這種生命週期方法的力量,而且合理的使用它。

阻止沒必要要的渲染只是其中一方面。上述結果告訴咱們如何提升渲染性能?

優雅的Rendering

下圖顯示了render()函數對應用程序性能的影響。渲染是複雜操做的開始,最終致使優化的DOM更新。

image

render越簡單,更新越快:

  • 執行的JavaScript代碼越少越好  — 特別是在移動網站/應用程序
  • 返回更少的更改將有助於加快 virtual DOM 的計算
  • 父組件 (container) 的render極可能會出發全部子孫組件的render(), 這意味着更多的計算。

image

如下是優化render階段的幾個提示:

  • 若是可能,跳過render
  • 在render函數外面,使用變量緩存開銷大的計算
  • … 或者將邏輯拆分進多個組件,而後有選擇地管理render方法
  • 儘量的返回簡單一點
  • 保持render方法的純粹 (使用函數式編程的思想)
  • 保證state的扁平化,使用淺比較

正如您的應用程序可能在render階段包含業務邏輯計算同樣,React還添加了helpers來加強您的開發體驗。讓咱們看看它們是如何影響性能的。

使用Production模式進行構建

React代碼默認爲development模式。使人驚訝的是,development環境下的渲染顯着較慢。

image

在構建應用程序時將其指定爲production模式的方法是定義環境變量NODE_ENV = production。固然,你只會在生產環境下用到這個,由於development 模式將提供更好的調試體驗。

如下是您在Webpack配置中自動執行此變量的方法:

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      ‘process.env’: {NODE_ENV: ‘」production」’}
    })
  ],
}

這樣作不只可以優化你的render性能,還可以提供更小的體積。

別忘了使用UglifyJS plugin剔除沒必要要的代碼,下面是一個使用例子:

plugins: [
  new webpack.optimize.UglifyJsPlugin({
    sourceMap: false,
    warnings: false,
  }),
],

咱們看到React 15在development模式下與以前的版本相比要慢得多。他們在生產中的比較結果又如何呢?

React 15 更快

React 15 重要更新 是框架與DOM交互的核心變化。 innerHTML 被替換成 document.createElement,現代瀏覽器中更快捷的選擇。
再也不支持Internet Explorer 8可能意味着一些內部流程更加優化。

React 15在渲染過程當中真的更快,但只有在構建模式爲production時才能生效。development模式實際上至關慢,主要是由於包含了太多的幫助調試和優化代碼的函數。
值得注意的是,這些發現是基於React 15與React-DOM 15的。React Native的development模式下結果可能會有顯着差別。也許你能夠運行相似的測試,並與社區分享結果。

寫在最後

性能開始於在React應用程序中優化render block。該基準測試比較了建立和優化組件的三種方法。你得對他們的使用場景和優缺點了然於心。

可能還有許多其餘的基準測試方法,所以絕對不要把你在這裏學到的一切看成理所固然。若是您有其餘想法歡迎貢獻。

Grgur是Modus Create的一名軟件架構師,專門從事JavaScript性能,React JS和Sencha框架。若是這篇文章有幫助,也許他16年的從財富100強到政府和初創公司的經驗能夠幫助你的項目。

相關文章
相關標籤/搜索