Redux的性能問題
轉載:Redux 中文文檔 Join the chat at https://gitter.im/camsong/redux-in-chinese
考慮到性能和架構, Redux 「可擴展性」 如何?
沒有一個明確的答案,在大多數情況下都不需要考慮該問題。
Redux 所做的工作可以分為以下幾部分:在 middleware 和 reducer 中處理 action (包括對象複製及不可變更新)、 action 分發之後通知訂閱者、根據 state 變化更新 UI 組件。雖然在一些複雜場景下,這些都 可能變成一個性能問題,但 Redux 本質上並沒有任何慢或者低效的實現。實際上,React Redux 已經做了大量的優化工作減少不必要的重複渲染,React Redux v5 相比之前的版本有著顯著的改進。
與其他庫相比,Redux 可能沒有那麼快。為了更大限度的展示 React 的渲染性能,state 應該以規範化的結構存儲,許多單獨的組件應該直接連接到 store,連接的列表組件應該將項目 ID 傳給子列表(允許列表項通過 ID 查找數據)。這使得要進行渲染的量最小化。使用帶有記憶功能的 selector 函數也對性能有非常大的幫助。
考慮到架構方面,事實證據表明在各種項目及團隊規模下,Redux 都表現出色。Redux 目前正被成百上千的公司以及更多的開發者使用著,NPM 上每月都有幾十萬的安裝量。有一位開發者這樣說:
規模方面,我們大約有500個 action 類型、400個 reducer、150個組件、5個 middleware、200個 action、2300個測試案例。
補充資料
文檔
- Recipes: Structuring Reducers - state 範式化
文章
- How to Scale React Applications (accompanying talk: Scaling React Applications)
- High-Performance Redux
- Improving React and Redux Perf with Reselect
- Encapsulating the Redux State Tree
- React/Redux Links: Performance - Redux
討論
- #310: Who uses Redux?
- #1751: Performance issues with large collections
- React Redux #269: Connect could be used with a custom subscribe method
- React Redux #407: Rewrite connect to offer an advanced API
- React Redux #416: Rewrite connect for better performance and extensibility
- Redux vs MobX TodoMVC Benchmark: #1
- Reddit: What's the best place to keep the initial state?
- Reddit: Help designing Redux state for a single page app
- Reddit: Redux performance issues with a large state object?
- Reddit: React/Redux for Ultra Large Scale apps
- Twitter: Redux scaling
- Twitter: Redux vs MobX benchmark graph - Redux state shape matters
- Stack Overflow: How to optimize small updates to props of nested components?
- Chat log: React/Redux perf - updating a 10K-item Todo list
- Chat log: React/Redux perf - single connection vs many connections
每個 action 都調用 「所有的 reducer」 會不會很慢?
我們應當清楚的認識到 Redux store 只有一個 reducer 方法。 store 將當前的 state 和分發的 action 傳遞給這個 reducer 方法,剩下的就讓 reducer 去處理。
顯然,在單獨的方法里處理所有的 action 僅從方法大小及可讀性方面考慮,就已經很不利於擴展了,所以將實際工作分割成獨立的方法並在頂層的 reducer 中調用就變得很有意義。尤其是目前的建議模式中推薦讓單獨的子 reducer 只負責更新特定的 state 部分。 combineReducers() 和 Redux 搭配的方案只是許多實現方式中的一種。強烈建議儘可能保持 store 中 state 的扁平化和範式化,至少你可以隨心所欲的組織你的 reducer 邏輯。
即使你在不經意間已經維護了許多獨立的子 reducer,甚至 state 也是深度嵌套,reducer 的速度也並不構成任何問題。JavaScript 引擎有足夠的能力在每秒運行大量的函數調用,而且大部門的子 reducer 只是使用 switch 語句,並且針對大部分 action 返回的都是默認的 state。
如果你仍然關心 reducer 的性能,可以使用類似 redux-ignore 和 reduxr-scoped-reducer 的工具,確保只有某幾個 reducer 響應特定的 action。你還可以使用 redux-log-slow-reducers 進行性能測試。
補充資料
討論
- #912: Proposal: action filter utility
- #1303: Redux Performance with Large Store and frequent updates
- Stack Overflow: State in Redux app has the name of the reducer
- Stack Overflow: How does Redux deal with deeply nested models?
在 reducer 中必須對 state 進行深拷貝嗎?拷貝 state 不會很慢嗎?
以不可變的方式更新 state 意味著淺拷貝,而非深拷貝。相比於深拷貝,淺拷貝更快,因為只需複製很少的欄位和對象,實際的底層實現中也只是移動了若干指針而已。
因此,你需要創建一個副本,並且更新受影響的各個嵌套的對象層級即可。儘管上述動作代價不會很大,但這也是為什麼需要維護範式化及扁平化 state 的又一充分理由。
Redux 常見的誤解: 需要深拷貝 state。實際情況是:如果內部的某些數據沒有改變,繼續保持統一引用即可。
補充資料
文檔
- Recipes: Structuring Reducers - Prerequisite Concepts
- Recipes: Structuring Reducers - Immutable Update Patterns
討論
- #454: Handling big states in reducer
- #758: Why can't state be mutated?
- #994: How to cut the boilerplate when updating nested entities?
- Twitter: common misconception - deep cloning
- Cloning Objects in JavaScript
怎樣減少 store 更新事件的數量?
Redux 在 action 分發成功(例如,action 到達 store 被 reducer 處理)後通知訂閱者。在有些情況下,減少訂閱者被調用的次數會很有用,特別在當 action 創建函數分發了一系列不同的 action 時。
如果你在使用 React,你可以寫在 ReactDOM.unstable_batchedUpdates() 以提高同步分發的性能,但這個 API 是實驗性質的,可能會在以後的版本中移除,所以也不要過度依賴它。可以看看一些第三方的實現 redux-batched-subscribe(一個高級的 reducer,可以讓你單獨分發幾個 action)、redux-batched-subscribe(一個 store 增強器,可以平衡多個分發情況下訂閱者的調用次數)和 redux-batched-actions(一個 store 增強器,可以利用單個訂閱提醒的方式分發一系列的 action)。
補充資料
討論
- #125: Strategy for avoiding cascading renders
- #542: Idea: batching actions
- #911: Batching actions
- #1813: Use a loop to support dispatching arrays
- React Redux #263: Huge performance issue when dispatching hundreds of actions
庫
- Redux Addons Catalog: Store - Change Subscriptions
僅有 「一個 state 樹」 會引發內存問題嗎?分發多個 action 會佔用內存空間嗎?
首先,在原始內存使用方面,Redux 和其它的 JavaScript 庫並沒有什麼不同。唯一的區別就是所有的對象引用都嵌套在同一棵樹中,而不是像類似於 Backbone 那樣保存在不同的模型實例中。第二,與同樣的 Backbone 應用相比,典型的 Redux 應用可能使用 更少 的內存,因為 Redux 推薦使用普通的 JavaScript 對象和數組,而不是創建模型和集合實例。最後,Redux 僅維護一棵 state 樹。不再被引用的 state 樹通常都會被垃圾回收。
Redux 本身不存儲 action 的歷史。然而,Redux DevTools 會記錄這些 action 以便支持重放,而且也僅在開發環境被允許,生產環境則不會使用。
補充資料
文檔
- Docs: Async Actions
討論
- Stack Overflow: Is there any way to "commit" the state in Redux to free memory?
- Reddit: What's the best place to keep initial state?
推薦閱讀:
※【譯】React如何抓取數據
※Webpack傻瓜指南(二)開發和部署技巧
※React首次渲染
※請問react中有什麼好用的ui庫嗎?
※React 模態框秘密和「輪子」漸進設計
TAG:React |