最近在這段時間瞭解了下redux,下面簡單記錄下所得。html
首先,仍是用幾句話簡單歸納下redux吧,歡迎拍磚。node
下面,先經過經典應用todos簡單介紹下redux的一些基礎概念react
state樹中存放一個應用程序內全部共享的數據,一個應用程序有且只有一個state。
todos的state結構爲git
{ todos: [], // 完整的任務列表 filter: 'SHOW_ALL' // 當前選中的任務過濾條件 }
action是惟一種改變state的方式,用來通知reducers修改state。
action是JavaScript plain object,描述「發生了什麼」。
默認action對象內部必須有type字段(字符串)來表示將要執行的動做。github
const ADD_TODO = 'ADD_TODO' { type: ADD_TODO, title: '你個標題黨' }
actionCreator就是生成action對象的函數,很是純的純函數。編程
function addTodo(title) { return { type: ADD_TODO, title } }
action對象負責描述」發生了什麼「,那reducer就是執行者,按照action對象的描述,
嚴格更新state。
reducer也是一種純函數,接受action對象和state樹做爲參數,生成新的state。redux
const reducer = (prevState, action) => nextState
這裏reducer採用純函數的意義是保證應用狀態的可預測性,只要傳入參數可知,那結果就可知。
同時nextState並非直接修改prevState所得,而是在prevState基礎上返回的一個新數組,經過
Object.assign或者immutable.js生成。這樣的目的是方便跟蹤全局應用狀態和回退/撤銷操做,
並且能夠在React組件內直接經過shouldComponentUpdate(nextProps, nextState)
進行對比。
因此,千萬不要「玷污」reducer函數:api
最後,隨着應用的複雜,state樹也會更加龐大,reducer內部的處理邏輯也會更加複雜。很難想象一堆代
碼根據action.type的可能值進行判斷處理。
咱們能夠經過分解、細化reducers來拆分數據處理邏輯,最後經過redux提供的combineReducers()
API來生成惟一的rootReducer。數組
const todoApp = combineReducers({ visibilityFilter, todos })
store就是將action和reducer聯繫在一塊兒的對象。babel
const = createStore(reducer, initState)
store對象有三種方法,經過這三種方法來維持應用的state。
至此,redux的核心方面已經說完。你可能發現這個redux彷佛和觀察者模式差很少呢。其實,它就是一個觀察者模式。
咱們能夠經過babel-node執行咱們的todos查看redux應用的調用過程。
ps: balel-node是node中安裝babel插件轉換ES6代碼,此時node v6.3.1還不支持export & import用法
上面redux調用流程圖:
redux只是一種應用狀態管理器,咱們須要經過react-redux結合React一塊兒使用才能開發出完整的應用。
react-redux是redux官方提供的一種綁定react的實現方案。主要提供了兩個api:
<Provider store>
包裹React頂層組件,而且爲子組件傳遞Redux store propsconnect([mapStateToProps], [mapDispatchToProps])
connect方法主要有可選參數,具體可參考官網API文檔,不過日常主要使用這兩個
{ addTodo, removeTodo, }
react組件內部經過this.props.addTodo('hello')
調用。
在介紹接下來的內容以前,插個已經被問爛的問題:什麼是js閉包和函數柯里化。此處只是簡單的舉個例子說明一下
let test = (a, b) => a + b let curryTest = a => b => a + b
好,接下來繼續扯咱們的,什麼是Middleware
此處,Redux經過Middleware實現對store.dispatch的封裝,擴展
該函數原有的功能,典型的是實現state日誌記錄的功能。
// 手動記錄logger功能代碼 console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState())
而redux經過Middleware創建一個store.dispatch的鏈條,每層middleware都會接受前一個middleware返回
的next(最初是store.dispatch), 而後在進行封裝,返回給後一個middleware調用的next,直到最後一個middleware返回
原始的store.dispatch處理action對象。
例如官網的logger middleware代碼:
const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
applyMiddleware(...middlewares)源碼:
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, initialState, enhancer) => { var store = createStore(reducer, initialState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
compose(..funcs)源碼
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } }
經過源碼咱們能發現,applyMiddleware接受的Middlewares數組除了最後一個middleware接受原始的store.dispatch(Array.prototype.reduceRight),其他middleware都會接受前一個middleware封裝後的next,因此此時的redux流程圖就是下面這樣:
介紹完redux的middleware,那redux的異步流程模式也就出來了。官網是經過redux-thunk Middleware實現的,咱們看下thunkMiddleware的源碼:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; }
這樣store.dispatch就能夠接受函數,也就能夠將其和網絡請求狀態動態綁定在一塊兒了。
具體源碼可參考官網async示例源碼。
本文參考連接: