Redux 的(簡短)歷史
2011 年:JS MVC 框架
早期 JavaScript MVC 框架,例如 AngularJS、Ember 和 Backbone,都有一些問題。AngularJS 嘗試強制將「控制器」與範本分開,但沒有任何東西可以阻止你在範本中撰寫 <div onClick="$ctrl.some.deeply.nested.field = 123">
。同時,Backbone 是基於事件發射器,模型、集合和檢視都各自有發射事件的能力。模型可能會發射 "change:firstName"
事件,而檢視會訂閱這些事件。但是,任何程式碼都可以訂閱這些事件並執行更多邏輯,這可能會觸發更多事件
這使得這些框架非常難以除錯和維護。更新一個模型中的一個欄位可能會觸發應用程式中數十個事件和邏輯,或者任何範本都可以在任何時候對狀態進行變更,這使得難以理解在執行狀態更新時會發生什麼事。
2014 年:Flux
大約在 2012-2013 年,當 React 首次公開發布時,Facebook 已在內部使用它幾年了。他們遇到的問題之一是,他們有多個獨立的 UI 片段需要存取相同的資料,例如「有多少未讀通知」,但他們發現使用 Backbone 風格的程式碼時,很難保持這個邏輯的正確性。
Facebook 最終想出了一個稱為「Flux」的模式:建立多個單例儲存,例如 PostsStore
和 CommentsStore
。這些儲存實例中的每一個都會向 Dispatcher
註冊,而觸發儲存中更新的唯一方法是呼叫 Dispatcher.dispatch({type: "somethingHappened"})
。那個純粹的物件稱為「動作」。這個想法是所有狀態更新邏輯都將半集中化 - 應用程式的任何隨機部分都不能變異狀態,並且所有狀態更新都將是可預測的。
Facebook 在 2014 年左右宣布了這個「Flux 架構」概念,但沒有提供實作該模式的完整函式庫。這導致 React 社群建立了數十個受 Flux 啟發的函式庫,並對模式進行了變更。
2015:Redux 的誕生
在 2015 年年中,Dan Abramov 開始建立另一個受 Flux 啟發的函式庫,稱為 Redux。這個想法是展示「時光旅行除錯」,用於研討會演講。這個函式庫被設計為使用 Flux 模式,但套用了某些函數式程式設計原則。你可以使用可預測的簡化器函式來進行不可變更新,而不是儲存實例。這將允許在時間中來回跳躍,以查看狀態在不同時間點的樣子。它還會使程式碼更直接、更可測試且更易於理解。
Redux 於 2015 年推出,並迅速消滅了所有其他受 Flux 啟發的函式庫。它在 React 生態系統中的高級開發人員中獲得了早期採用,到 2016 年,許多人開始說「如果你正在使用 React,你也必須使用 Redux」。(坦白說,這導致許多人在不需要使用 Redux 的地方使用了 Redux!)
還值得注意的是,當時 React 只有其舊版 Context API,它基本上是損壞的:它無法正確傳遞更新的值。因此,可以將事件發射器放入 Context 並訂閱它們,但你無法真正將它用於純粹的資料。這意味著許多人開始採用 Redux,因為它是一種在整個應用程式中一致傳遞更新值的方法。
Dan 早期就說過,「Redux 並不是寫程式碼的最短途徑,而是讓程式碼可預測且易於理解」。部分原因在於它具有一致的模式(狀態更新由 reducer 執行,因此你總是查看 reducer 邏輯,以了解狀態值可能是什麼、可能的動作是什麼,以及它們會造成什麼更新)。這也與將邏輯移出元件樹有關,因此 UI 大多只會說「這件事發生了」,而你的元件會更簡單。此外,以「純函數」編寫的程式碼,例如 reducer 和 selector,更易於理解:輸入參數、輸出結果,無需查看其他內容。最後,Redux 的設計啟用了 Redux DevTools,它會顯示所有已發送動作的可讀取清單、動作/狀態包含的內容,以及每個動作發生的變更。
早期的 Redux 模式特別繁瑣。通常會有 actions/todos.js
、reducers/todos.js
和 constants/todos.js
,僅用於定義單一動作類型(const ADD_TODO = "ADD_TODO"
)、動作建立函式和 reducer 案例。你還必須手動使用擴散運算子撰寫不可變更新,這很容易出錯。人們確實會在 Redux 中擷取和快取伺服器狀態,但撰寫 thunk 以執行擷取、使用擷取的資料發送動作,以及在 reducer 中管理快取狀態,需要大量手動編寫的程式碼。
儘管有這些繁瑣的程式碼,Redux 還是變得流行,但這一直是最大的疑慮。
2017:生態系競爭
到了 2017-18 年,情況發生了變化。現在許多社群更專注於「資料擷取和快取」,而非「用戶端狀態管理」,而這正是我們看到 Apollo、React Query、SWR 和 Urql 等資料擷取函式庫興起的時候。同時,我們也迎來了新的 React Context API,它可以正確地將更新的值傳遞到元件樹中。
這表示 Redux 不再像以前一樣「必要」了,現在有其他工具可以解決許多相同的問題,重疊程度不一(而且通常程式碼更少)。關於「繁瑣程式碼」的頻繁抱怨也讓許多使用 Redux 的人感到擔憂。
2019:Redux Toolkit
因此,在 2019 年,我們建置並發布了 Redux Toolkit,作為使用較少程式碼撰寫相同 Redux 邏輯的更簡單方式。RTK 仍然是「Redux」(單一儲存體,透過不可變更新邏輯在 reducer 中觸發狀態更新的調度動作),但具有更簡單的 API 和更好的內建預設行為。這也包括 RTK Query,我們的內建資料擷取和快取函式庫,靈感來自 React Query 和 Apollo。
如今,RTK 是撰寫 Redux 邏輯的標準方式。與所有工具一樣,它有其優缺點。RTK 可能會比 Zustand 使用更多程式碼,但它也提供有用的模式,用於將應用程式邏輯與 UI 分開。Redux 並非所有應用程式的合適工具,但它仍然是 React 應用程式中使用最廣泛的狀態管理程式庫,具有絕佳的文件,並提供許多功能,協助您建置結構一致且可預測的應用程式。