我以前開發網站的時候一直用的是 Flux, 自從出了 Redux 以後因爲種種緣由沒有跟進了解,最近手頭上的事情基本忙的差很少了,抽空閱讀了 Redux 的源碼,並整理了這篇博文。javascript
先說重點: Redux 與 React 沒有關係,就好像 Javascript 和 Java ,雷鋒和雷峯塔的關係同樣。 Redux 旨在處理數據的流動。java
Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。 Redux 是由 Flux 演變而來。git
那麼 Flux 是什麼? Flux 在這裏並非一個框架,而是提供了一套數據流動的方案,相似 MVVM 的概念。github
Redux 是一個狀態容器,這句話挺難理解的,下面的分析也是個人我的看法,不見得正確,歡迎指正。算法
咱們知道狀態機是表示有限個狀態以及在這些狀態之間的轉移和動做等行爲的數學模型。編程
例子:
有一個提供簡單加減計算的二則運算的算法。初始值爲0,能夠增長一個值和減去一個值。redux
能夠以下本身實現一個狀態機:框架
const fsm = { currentState: 0, create(state) { this.currentState = state }, getState() { return this. currentState }, transition(action) { switch(action.type) { case 'add': this.currentState = this.currentState + action.num break case 'sub': this.currentState = this.currentState - action.num break default: break } } } fsm.create(5) fsm.transition({'type':'add', 'num':-1}) console.log(fsm.getState()) // ==> 4 fsm.transition({'type':'sub', 'num':1}) console.log(fsm.getState()) // ==> 3
從上面的例子能夠看到狀態的改變方式爲:輸入初始狀態值5,此時的 currentState 爲0,輸入{'type':'add', 'num': 1}
,通過條件判斷是要將狀態值 5 加 -1 變成 4,再輸入{'type':'sub', 'num': 1}
,通過條件判斷是要將狀態值 4 減 1 變成 3。async
對比 Redux 來看的話, 咱們的 fsm 就是 Redux 的 createStore 返回的 store,store.getState() 返回的狀態對應 fsm.getState()。 那麼 reducer + dispatch + action 對應的就是 fsm.transition()。以後會咱們分析源碼看看 Redux 是怎樣把 reducer + dispatch + action 轉成 fsm.transition。函數式編程
整理了一張 Redux 的狀態圖以下:
對於 Redux 來講,就是把數據當成狀態來處理,reducer 就是根據行爲(action) 將當前數據(狀態)轉成新的狀態,新的數據狀態能夠繼續被 reducer 處理。
Action 是把數據從應用傳到 stateTree(狀態樹)的輸入動做(payloads)。按照約定來講 action 是一個帶有 type 屬性的 javascript plain object
,對應着 Flux 中的 payload。
Action Creator 是一個建立 Action 的函數,額,其實就是函數式編程搞出的概念,把一個表達式包裝成一個函數,返回這個 Action。
對照上面咱們本身寫的狀態機代碼能夠看出 action 的做用告訴 statetree (狀態樹)發生什麼變化,及所須要的數據是什麼。
Reducer 的是根據 action 來決定數據應該變化成什麼樣子的函數,即將上面 fsm 中的switch case 表達式包裝而成的函數。
dispatch 是更新狀態樹的方法,在 dispatch 中會調用 reducer, 且通知監聽者數據已發生變化。
從上面的分析應該能夠推斷出 Redux 暴露的 dispatch 會接受一個 action,來決定根據 reducer 去轉換狀態樹,那麼也能夠推斷出 Redux 必定也須要提供一個接受 reducer 函數的API。
Redux 提供的 createStore(reducers, initialState)
API 確實如咱們推斷,會在此時傳入 reducer,以及一個可選的初始狀態。 createStore
返回的是一個 store
, store 和狀態樹是不一樣的,此處的store具備dispatch(action)
方法的對象,真正的狀態樹是 store.getState()(也就是咱們真正要使用的數據)。
export default function createStore(reducer, initialState) { // 在調用 createStore 的時候,必須傳入 reducer, 且 reducer 必須爲函數 if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } var currentReducer = reducer var currentState = initialState var listeners = [] var isDispatching = false // 返回此時的狀態 function getState() { return currentState } // 訂閱函數,調用 dispatch 的時候會調用 listener function subscribe(listener) { // ... } // 發佈函數, 在 action 觸發狀態的改變後,通知全部訂閱的 listener function dispatch(action) { // 傳入的 action 必須爲 plain object,也就是 action creator 返回的對象 // 本身傳入 action 對象也是能夠的 // 可是 Redux 推薦的寫法是 action creator 的寫法 // 至於寫成函數的好處不在這裏討論 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // 強制要求 action 必須帶入 type 屬性,比 Flux 有更強的約束 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true // 這裏就是把 action 和當前狀態通過 reducer 處理以後返回一個新的狀態 // currentReducer 就是 createStore 傳進來的 reducer // 能夠切回去看看上面我總結的圖 currentState = currentReducer(currentState, action) } finally { isDispatching = false } // 通知訂閱的事件 listeners.slice().forEach(listener => listener()) return action } // 狀態初始話,此時的 Action 爲 { type: ActionTypes.INIT } dispatch({ type: ActionTypes.INIT }) // createStore 最後返回一個罕有 dispatch 和 getState 的對象 return { dispatch, subscribe, getState, replaceReducer } }
Redux 就像是做者本身的介紹,是一個 JavasSript 的狀態容器,全部的數據(狀態)的變化都是當前狀態和 Action 共同的做用結果。 對於使用者(通常都是指 view)來講,不用關心數據是怎樣變化,只須要在 view 層面等待 store 通知本身數據發生變化,而後把數據渲染成頁面便可。
這裏沒有提到 Redux 的另外一個比較重要也比較難理解的 Middleware。由於若是在這裏說的的話,文章不知道要寫多長,而長文我如今也駕馭不住,因此乾脆就不寫了,後面我會再補一篇理解 Middleware 的文章。
其實我對 Redux 的這種實現狀態的方式並不太喜歡,相對來講 javascript-state-machine 看起來更舒服一些,不知道和 Redux 結合有什麼效果。可能畫面太美,我不敢想?。
原文 @github
做者 @zwhu