Redux
是一種前端「架構模式」,是 Flux
架構的一種變種,用來提供可預測的狀態管理。雖然常常和 React
一塊兒被說起,可是 Redux
卻不只僅只能用於 React
,還能夠將其運用到其餘前端庫中,Vue
Angular
甚至是 jQuery
。Redux
只是一種架構模式而已,並無和其餘庫綁定在一塊兒。而 React-redux
就是把 Redux
和 React.js
結合起來的一個庫。就像 Vuex
同樣,是一個與 Vue.js
結合的 Flux
變種。前端
也許有人會問:爲何咱們會須要 redux
呢? 嗯... 確實,咱們必需要先了解咱們爲何須要 redux
? redux
的出現是爲了解決什麼問題?react
那麼,咱們來考慮這麼一種場景,在你構建的一棵組件樹中,有A、B那麼兩個組件,它們須要共享同一個狀態,你會怎麼辦呢?redux
咱們能夠經過狀態提高的思路,將該狀態提高到附近的公共父組件上面,而後經過 props
把狀態傳遞給子組件,這樣就能夠在A
、B
組件之間共享數據了。確實能夠,可是若是A
、B
的父組件在組件樹向上好幾個組件的位置呢?就須要將狀態經過 props
一級一級往下傳遞,那麼狀態的傳遞路徑就會很是長,並且中間組件根本就不須要訪問這個狀態。並且,若是後續有一個C
組件也要訪問該狀態而且A
、B
、C
的公共父組件還要往上呢?你就不得不修改以前代碼了。很顯然,這不是一種很好的解決方案,它會讓咱們的代碼維護起來很是痛苦。微信
難道就沒有其餘方法能夠解決這個問題嗎?其實也有的,那就是 react
的 context
,一個組件只要往本身的 context
裏面放了某些狀態,那麼這個組件的全部子組件均可以直接訪問這個狀態而不須要經過中間組件的傳遞,看起來問題解決了嘛。架構
咱們雖然解決了狀態傳遞的問題卻引入了新的問題,咱們引入的 context
打破了組件和組件之間經過 props
傳遞數據的規範,極大地加強了組件之間的耦合性。並且 context
就像全局變量同樣,裏面的數據能夠被子組件隨意更改,可能會致使程序不可預測的運行。函數
這時候咱們就該考慮使用 Redux
了,Redux
能夠幫你建立應用的共享狀態,而且不能隨意的更改這些狀態。spa
咱們已經瞭解了爲何要使用 Redux
,那麼咱們先了解下 Redux
的三個基本概念。
設計
咱們能夠經過 createStore 來建立 storecode
import { createStore } from 'redux'; const store = createStore(reducers);
在 Redux
中,應用程序只能擁有一個 store
,用來保存整個應用程序的 state
,至關於一個應用程序的共享狀態。對象
咱們能夠經過 store.getState()
來獲取應用程序的當前狀態。可是咱們卻不能隨意的修改狀態,咱們只能經過 store.dispatch(action)
來修改狀態。
修改完狀態以後,咱們但願能夠作些 view
層的改變,這時能夠經過 store.subscribe(() => {})
來註冊視圖變化的回調函數。
Actions
是一個 JavaScript
普通對象,用來描述應用程序中發生的一些事情,也是把數據從應用傳遞給 store
的惟一途徑。
咱們約定,action
內必須使用一個字符串類型的 type
字段來表示將要執行的動做,type
通常會被定義成字符串常量。
const ADD_TODO = 'ADD_TODO' { type: ADD_TODO, data: 'some data' }
咱們除了直接以 JavaScript
普通對象的形式來定義 action
以外,也能夠經過函數形式來定義 action
,這個函數被稱做 Action
建立函數( actionCreator
)。
const ADD_TODO = 'ADD_TODO'; function addTodo(data) { return { type: ADD_TODO, data } }
這裏 action
建立函數 addTodo
很簡單,只是返回一個 action
。 咱們能夠經過 store.dispatch
來通知須要修改狀態
store.dispatch(addTodo('some data'));
咱們已經知道能夠經過 action
來修改狀態,可是 action
傳遞過來的只是簡單的對象,並無具體處理狀態的邏輯,這就是 reducers
要作的事情了。
Reducer
必須是一個純函數(一個函數的返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,這個函數就叫作純函數)。由於純函數很是「靠譜」,執行一個純函數不會產生不可預料的行爲,也不會對外部產生影響。
function todoApp(state = { title: 'todoApp', todos: [] }, action) { switch (action.type) { case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { data: action.data, completed: false } ] }) default: return state } }
todoApp
接收舊的 state
和 action
,並返回新的 state
。注意這裏咱們是將 state
拷貝一份,再添加咱們改動的值去覆蓋原來的數據,從新組合成新的 state
返回,而不是直接修改 state
。
這是由於若是你直接去改變 state
裏對象的屬性,那麼就須要去比較新舊兩個 state
的區別,而比較兩個 Javascript
對象全部的屬性是否相同就須要對它們進行深比較。可是在真實的應用中 js
的對象都很大,進行深比較的代價十分昂貴。而若是你返回的是一個全新的對象,就只須要比較新舊兩個對象的存儲地址是否相同就能夠了。Redux
就是這麼作的,若是你在 reducer
內部直接修改舊的 state
對象的屬性值,那麼新的 state
和舊的 state
將都指向同一個存儲地址,Redux
會認爲沒有任何改變。
咱們能夠有多個 reducer
,每一個 reducer
只負責管理全局 state
中它負責的那部分,每一個 reducer
的 state
參數能夠都不一樣,分別對應它管理的那部分 state
數據。而後經過 combineReducers
組成根 reducer
用來建立一個 store
。
import { combineReducers } from 'redux'; const todosReducer = (state = [], action) => { // do something } const titleReducer = (state = '', action) => { // do something } const reducer = combineReducers({ todos: todosReducer, title: titleReducer }); // 等價於 function reducer(state = {}, action) { return { todos: todosReducer(state.todos, action), title: titleReducer(state.title, action) } }
combineReducers
這個函數會調用你的定義的 reducer
,每一個 reducer
根據它們的 key(todos, title)
來篩選出 state
中的一部分數據處理並返回一份副本,根 reducer
會把這些副本組合起來造成一個新的大對象。最後根 reducer
將這個大對象傳回給 store
,store
再將它設爲最終的狀態。
redux
一些基本概念咱們都清楚了,咱們來總結一下,Redux
爲咱們所作的事情:
state
的地方actions
經過純函數修改應用程序共享 state
的機制state
更新的機制嚴格的單向數據流是 Redux
架構的設計核心。
咱們只要清楚了 redux
中的數據流動的過程就明白 redux
整個工做流程了,咱們從產生一個 action
的切入點來分析數據是怎樣流動的。
action
,這個 action
多是經過 actionCreator
返回的。store
接受這這個 action
以後,將當前的 state
和 action
一塊兒傳遞給根 reducer
。reducer
將 state
分配給子 reducer
進行處理,子 reducer
返回修改後的副本給根 reducer
,根 reducer
整合子 reducer
返回的副本生成一個新的 state
副本返回給 store
。store
根據新的 state
觸發視圖層的渲染。action
...
更多精彩內容,歡迎關注微信公衆號~