React.js 效能實驗: 當 React.js 遇到 10000 個 checkboxes

陳冠霖
7 min readJul 20, 2016

--

其實這篇文章是要為 不要只因為性能考量而選擇 React.js 中對 React 操作大量的 DOM 的效能辯白,是的 React 沒有你想像中的快,但是其實也沒有文章中的 例子 那麼誇張,文章中的例子是在開發環境下跑的,我實際上測試全選 10000 個 checkbox 在 production 環境情形下速度可以比開發環境模式快上 4 ~ 5 倍左右

這篇文章會比較一些提高速度的作法,並實際量測 js 運行時間

  • 一般沒有下環境變數的 Dev 模式
  • 下環境變數 NODE_ENV = production
  • 減少 Component 的抽象數量
  • 用 Redux 減少 Reconciliation 的 Component 數量

這個實驗code在此 react-long-list 你可以自行 clone下來跑看看,以下是在 chrome canary 下開 Timeline 觀察到的 js 執行時間

               initial time    select all   select one
---------------------------------------------------------
dev 3150 ms 1960 ms 1860 ms
---------------------------------------------------------
production 827 ms 314 ms 241 ms
---------------------------------------------------------
no-Abstract 734 ms 164 ms 160 ms
---------------------------------------------------------
Redux 1150 ms 359 ms 78 ms
---------------------------------------------------------

不過其實這個例子的效能沒有那麼重要,畢竟一個頁面設計 10000 個 checkbox 蠻有病的,效能不是工程師要考慮的唯一問題,其他還有維護與同事協作的方便性,運行容不容易出錯,出錯容不容易除錯,而大部分的台灣老闆只在乎開發的速度跟功能的完成,效能上在使用不要太誇張老闆通常也不會很介意(嘆)

在 Dev 模式下開發

在一般的情形下跑

npm run dev

初始化 10000 個 checkbox 的 js 運行時間約為 3.15 s

全選 10000 個 checkbox js 運行時間大約是 1.96 sec

單選 1 個 checkbox js 運行時間大約為 1.86 秒

NODE_ENV = production

如果你在 React Github 的官方 Repository 搜尋 __DEV__ 這個變數,可以發現大概有 70 個搜尋結果,有許多 type 還有參數的 validate 的檢查 warning都被夾住在 if (__DEV__) 的判斷式下來幫助我們開發,官方在 build code 到npm 上的時候會用 babel 把 __DEV__ 這個變數用 process.env.NODE_ENV !== ‘production’ 取代,所以如果用在用 webpack 打包 code 的時候,記得要用 DefinePlugin 把環境變數設成 production

new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production'),
},
}),

另外 UglifyJS 的時候把 Dead Code compression 打開就會直接移除整個 if(false) code block

new webpack.optimize.UglifyJsPlugin({
compress:{
dead_code: true,
}
})

用這樣的設定跑

npm run production

初始化 10000 個 checkbox 的 js 運行時間約莫是 827 ms

全選 10000 checkbox 的 js 運行時間為 314 ms 左右

單選一個 checkbox 的 js 運行時間 241 ms 左右

任何的 Abstraction 都會造成效能上的損失

原本的架構上把 checkbox 做成一個 component ,但是其實越多的Component 都會造成對效能產生損失,因此這次我們把 checkBox Component 拆掉,直接在 App 裡面 render function map 出所有的 checkbox,運行

npm run no-abstract

實驗結果如下

初始化 10000 個 checkbox js 運行時間約為 734 ms

全選 10000 checkbox 的 js 運行時間 164 ms 左右:

單選 1 個 checkBox 的 js 運行時間 160 ms 左右:

用 Redux connect 每一個 checkbox 減少不必要的 Reconciliation 成本

React 從上層 Component seState 會造成下方所有的 Components 都會進到Reconciliation 的過程來決定要不要更新,因此我想如果一開始就不要進入Reconciliation 會不會速度會更快一點,因此我就改用 Redux 去托管 state,react-redux 用 connect 包住 checkbox 後 call setState 的就會變成 connect 裡頭的 higher order component ,而非原本的上層 Component,這樣進入 Reconciliation 的 Component 只有被點選的 checkbox 。 但是其實這樣會不會加速其實也很難說,redux 本身也是會有 reducer 跟 listener array 需要跑的成本,運行

npm run redux

實驗結果如下:

初始化 10000個 checkbox 的 js 運行時間 1.15 s

全選 10000 checkbox 的 js 運行時間 359 ms 左右:

單選一個 checkbox 的 js 運行時間 78.4 ms,結果看起來單選的確提昇不少:

結論

production build 的時候一定記得要設 NODE_ENV= production

其他的優化還要考慮其他架構上的設計,不見得一定要追求到這種地步,也許你也沒有如此大量的 component 要 update,幫助可能更有限,大家參考一下就好

--

--

陳冠霖
陳冠霖

Written by 陳冠霖

Yahoo Taiwan Sr. Frontend Engineer. Write something about web and React.js here.

No responses yet