Redux · 簡介
已發表: 2022-03-10Redux 是當今前端開發中最熱門的庫之一。 然而,許多人對它是什麼以及它的好處是什麼感到困惑。
正如文檔所述,Redux 是 JavaScript 應用程序的可預測狀態容器。 換句話說,它是一種應用程序數據流架構,而不是傳統的庫或像 Underscore.js 和 AngularJS 這樣的框架。
進一步閱讀SmashingMag
- 為什麼你應該為你的移動應用考慮 React Native
- 應用程序、遊戲和移動網絡的測試自動化
- 使用 React、Node 和 Express 進行服務器端渲染
- 關於客戶端呈現的可訪問性的註釋
Redux 由 Dan Abramov 於 2015 年 6 月左右創建。它的靈感來自 Facebook 的 Flux 和函數式編程語言 Elm。 Redux 因其簡單、體積小(僅 2 KB)和出色的文檔而迅速流行起來。 如果您想了解 Redux 內部的工作原理並深入了解該庫,請考慮查看 Dan 的免費課程。
Redux 主要用於應用程序狀態管理。 總而言之,Redux 將整個應用程序的狀態維護在一個不可變的狀態樹(對象)中,不能直接更改。 當某些事情發生變化時,會創建一個新對象(使用動作和減速器)。 我們將在下面詳細討論核心概念。
它與 MVC 和 Flux 有何不同?
為了給出一些觀點,讓我們採用經典的模型-視圖-控制器 (MVC) 模式,因為大多數開發人員都熟悉它。 在 MVC 架構中,數據(模型)、表示(視圖)和邏輯(控制器)之間有明確的分離。 這樣做有一個問題,尤其是在大規模應用程序中:數據流是雙向的。 這意味著一個更改(用戶輸入或 API 響應)可能會影響代碼中許多地方的應用程序狀態 - 例如,雙向數據綁定。 這可能很難維護和調試。
Flux 與 Redux 非常相似。 主要區別在於 Flux 有多個更改應用程序狀態的存儲,並將這些更改作為事件廣播。 組件可以訂閱這些事件以與當前狀態同步。 Redux 沒有 dispatcher ,它在 Flux 中用於將有效負載廣播到已註冊的回調。 Flux 的另一個不同之處在於有很多品種可供選擇,這會造成一些混亂和不一致。
Redux 的好處
你可能會問,“為什麼我需要使用 Redux?” 好問題。 在你的下一個應用程序中使用 Redux 有幾個好處:
- 結果的可預測性
總有一個事實來源,即存儲,對於如何將當前狀態與操作和應用程序的其他部分同步,這一點毫不費力。 - 可維護性
具有可預測的結果和嚴格的結構使代碼更易於維護。 - 組織
Redux 對代碼的組織方式更加嚴格,這使得代碼更一致,更容易讓團隊使用。 - 服務器渲染
這非常有用,尤其是對於初始渲染,可以提供更好的用戶體驗或搜索引擎優化。 只需將在服務器上創建的商店傳遞給客戶端即可。 - 開發者工具
開發人員可以實時跟踪應用程序中發生的一切,從動作到狀態變化。 - 社區和生態系統
每當您學習或使用任何庫或框架時,這都是一個巨大的優勢。 擁有 Redux 背後的社區使其更具吸引力。 - 易於測試
編寫可測試代碼的第一條規則是編寫只做一件事且獨立的小函數。 Redux 的代碼大多是函數,它們就是:小、純、隔離。
函數式編程
如前所述,Redux 建立在函數式編程概念之上。 理解這些概念對於理解 Redux 如何以及為什麼以它的方式工作非常重要。 讓我們回顧一下函數式編程的基本概念:
- 它能夠將函數視為一等對象。
- 它能夠將函數作為參數傳遞。
- 它能夠使用函數、遞歸和數組來控制流程。
- 它能夠使用純函數、遞歸函數、高階函數、閉包函數和匿名函數。
- 它能夠使用輔助函數,例如 map、filter 和 reduce。
- 它能夠將功能鏈接在一起。
- 狀態不會改變(即它是不可變的)。
- 代碼執行的順序並不重要。
函數式編程允許我們編寫更簡潔、更模塊化的代碼。 通過編寫在範圍和邏輯上隔離的更小更簡單的函數,我們可以使代碼更容易測試、維護和調試。 現在這些更小的函數變成了可重用的代碼,這讓你可以寫更少的代碼,更少的代碼是一件好事。 這些功能可以在任何地方復制和粘貼而無需任何修改。 在範圍內隔離且僅執行一項任務的功能將更少地依賴應用程序中的其他模塊,而這種減少的耦合是函數式編程的另一個好處。

在使用函數式 JavaScript 時,您會經常看到純函數、匿名函數、閉包、高階函數和方法鍊等。 Redux 大量使用純函數,因此了解它們是什麼很重要。
純函數根據傳遞給它們的參數返回一個新值。 他們不修改現有對象; 相反,他們返回一個新的。 這些函數不依賴於調用它們的狀態,並且對於任何提供的參數,它們只返回一個相同的結果。 出於這個原因,它們是非常可預測的。
因為純函數不修改任何值,所以它們不會對作用域或任何可觀察到的副作用產生任何影響,這意味著開發人員可以只關注純函數返回的值。
Redux 可以在哪裡使用?
大多數開發人員將 Redux 與 React 相關聯,但它可以與任何其他視圖庫一起使用。 例如,您可以將 Redux 與 AngularJS、Vue.js、Polymer、Ember、Backbone.js 和 Meteor 一起使用。 不過,Redux 加 React 仍然是最常見的組合。 確保以正確的順序學習 React:最好的指南是 Pete Hunt 的,這對於剛開始使用 React 並且對生態系統中發生的一切感到不知所措的開發人員非常有幫助。 JavaScript 疲勞是前端開發人員的一個合理問題,無論是新手還是經驗豐富的開發人員,因此請花時間以正確的順序以正確的方式學習 React 或 Redux。
Redux 很棒的原因之一是它的生態系統。 有如此多的文章、教程、中間件、工具和样板可供使用。 就個人而言,我使用 David Zukowski 的樣板文件,因為它擁有構建 JavaScript 應用程序所需的一切,包括 React、Redux 和 React Router。 提醒一句:在學習 React 和 Redux 等新框架時,盡量不要使用樣板文件和入門工具包。 這會讓事情變得更加混亂,因為你不會理解一切是如何協同工作的。 首先學習它並構建一個非常簡單的應用程序,最好作為一個輔助項目,然後將樣板用於生產應用程序以節省時間。
構建 Redux 的組成部分
Redux 概念可能聽起來很複雜或很花哨,但它們很簡單。 請記住,該庫只有 2 KB。 Redux 具有三個構建部分:actions、store 和 reducer。

讓我們討論一下每個人的作用。
行動
簡而言之,動作就是事件。 操作將數據從應用程序(用戶交互、API 調用等內部事件和表單提交)發送到存儲。 商店僅從操作中獲取信息。 內部操作是具有type
屬性(通常是常量)的簡單 JavaScript 對象,描述了操作的類型和發送到存儲的信息的有效負載。
{ type: LOGIN_FORM_SUBMIT, payload: {username: 'alex', password: '123456'} }
動作是由動作創建者創建的。 這聽起來很明顯,我知道。 它們只是返回動作的函數。
function authUser(form) { return { type: LOGIN_FORM_SUBMIT, payload: form } }
因此,在應用程序的任何位置調用操作都非常容易。 使用dispatch
方法,像這樣:

dispatch(authUser(form));
減速機
我們已經討論過函數式 JavaScript 中的 reducer 是什麼。 它基於數組 reduce 方法,它接受回調(reducer)並讓您從多個值、整數總和或值流的累積中獲取單個值。 在 Redux 中,reducers 是函數(純函數),它們獲取應用程序的當前狀態和一個操作,然後返回一個新狀態。 了解 reducer 的工作方式很重要,因為它們執行大部分工作。 這是一個非常簡單的reducer,它將當前狀態和一個動作作為參數,然後返回下一個狀態:
function handleAuth(state, action) { return _.assign({}, state, { auth: action.payload }); }
對於更複雜的應用程序,可以使用 Redux 提供的combineReducers()
實用程序(確實,推薦)。 它將應用程序中的所有減速器組合成一個索引減速器。 每個reducer都負責app狀態的自己的一部分,並且每個reducer的狀態參數是不同的。 combineReducers()
實用程序使文件結構更易於維護。
如果一個對象(狀態)只改變了一些值,Redux 會創建一個新對象,沒有改變的值將引用舊對象,並且只會創建新值。 這對性能非常有用。 為了提高效率,您可以添加 Immutable.js。
const rootReducer = combineReducers({ handleAuth: handleAuth, editProfile: editProfile, changePassword: changePassword });
店鋪
Store 是保存應用程序狀態的對象,並提供一些輔助方法來訪問狀態、調度操作和註冊偵聽器。 整個狀態由單個商店表示。 任何操作都通過 reducer 返回一個新狀態。 這使得 Redux 非常簡單和可預測。
import { createStore } from 'redux'; let store = createStore(rootReducer); let authInfo = {username: 'alex', password: '123456'}; store.dispatch(authUser(authInfo));
開發者工具、時間旅行和熱重載
為了使 Redux 更易於使用,尤其是在處理大型應用程序時,我建議使用 Redux DevTools。 它非常有用,可以顯示狀態隨時間的變化、實時變化、動作和當前狀態。 這樣可以避免console.log
的當前狀態和操作,從而節省您的時間和精力

Redux 的時間旅行實現與 Flux 略有不同。 在 Redux 中,您可以返回到以前的狀態,甚至可以從那時起將您的狀態轉向不同的方向。 Redux DevTools 支持 Redux 工作流程中的以下“時間旅行”功能(將它們視為您狀態的 Git 命令):
- 重置:重置為您的商店創建時的狀態
- Revert : 回到上次提交的狀態
- Sweep :刪除您可能錯誤觸發的所有禁用操作
- Commit :使當前狀態成為初始狀態
時間旅行功能在生產中效率不高,僅用於開發和調試。 DevTools 也是如此。
Redux 使測試變得更加容易,因為它使用函數式 JavaScript 作為基礎,並且小的獨立函數很容易測試。 因此,如果您需要更改狀態樹中的某些內容,請僅導入一個負責該狀態的 reducer,並單獨對其進行測試。
構建應用程序
結束這個介紹性指南,讓我們使用 Redux 和 React 構建一個非常簡單的應用程序。 為了讓大家更容易理解,我將堅持使用普通的舊 JavaScript,盡可能少地使用 ECMAScript 2015 和 2016。 我們將繼續本文前面開始的登錄邏輯。 這個例子沒有使用任何實時數據,因為這個應用程序的目的是展示 Redux 如何管理一個非常簡單的應用程序的狀態。 我們將使用 CodePen。
1.反應組件
我們需要一些 React 組件和數據。 讓我們製作一個簡單的組件並將其呈現在頁面上。 該組件將有一個輸入字段和一個按鈕(這是一個非常簡單的登錄表單)。 下面,我們將添加代表我們狀態的文本:
請參閱 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux。
2. 事件和動作
讓我們將 Redux 添加到項目中並處理按鈕的onClick
事件。 一旦用戶登錄,我們將使用LOGIN
類型和當前用戶的值來調度操作。 在我們這樣做之前,我們必須創建一個 store 並將一個 reducer 函數作為參數傳遞給它。 現在,reducer 只是一個空函數:
請參閱 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 2.Events and Actions。
3.減速機
現在我們已經觸發了動作,reducer 將執行該動作並返回一個新狀態。 讓我們處理返回登錄狀態的LOGIN
操作,並添加一個LOGOUT
操作,以便我們以後可以使用它。 auth
reducer 接受兩個參數:
- 當前狀態(具有默認值),
- 那個行動。
請參閱 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 3. Reducers。
4. 顯示當前狀態
現在,我們已經有了初始狀態(reducer 中的默認值)和 React 組件,讓我們看看狀態的樣子。 最佳實踐是將狀態下推到子組件。 因為我們只有一個組件,所以讓我們將應用程序的狀態作為屬性傳遞給auth
組件。 為了讓所有東西一起工作,我們必須使用subscribe
輔助方法註冊 store 監聽器,方法是將ReactDOM.render
包裝在一個函數中並將其傳遞給store.subscribe()
:
請參閱 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 4. Displaying current state。
5.登錄和退出
現在我們有了登錄和註銷操作處理程序,讓我們添加一個註銷按鈕並調度LOGOUT
操作。 最後一步是管理顯示登錄或註銷的按鈕,方法是將此登錄移動到渲染方法之外並在下方渲染變量:
請參閱 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 5. Login/Logout。
結論
Redux 每天都在受到關注。 它已被許多公司(Uber、Khan Academy、Twitter)和許多項目(Apollo、WordPress' Calypso)使用,並成功地投入生產。 一些開發人員可能會抱怨有很多開銷。 在大多數情況下,需要更多代碼來執行簡單的操作,例如按鈕單擊或簡單的 UI 更改。 Redux 並不適合所有事情。 必須有一個平衡。 也許簡單的操作和 UI 更改不必成為 Redux 存儲的一部分,並且可以在組件級別進行維護。
儘管 Redux 可能不是您的應用程序或框架的理想解決方案,但我強烈建議您檢查一下,尤其是對於 React 應用程序。
首頁圖片來源:Lynn Fisher,@lynnandtonic