標籤:com 計算 war div gpo import 隨筆 列印 pre
現在我們的Redux和React-Redux已經基本實現了,在Redux中,觸發一個action,reducer立即就能算出相應的state,如果我要過一會才讓reducer計算state呢怎麼辦?也就是我們如何?非同步action呢?這裡就要用到中介軟體(middleware)
1. 中介軟體(middleware)介紹
中間就是在action與reducer之間又加了一層,沒有中介軟體的Redux的過程是:action -> reducer
,而有了中介軟體的過程就是action -> middleware -> reducer
,使用中介軟體我們可以對action也就是對dispatch方法進行裝飾,我們可以用它來實現非同步action、列印日誌、錯誤報表等功能。
又是裝飾器,沒錯,這塊的好多東西都離不開裝飾器模式,所以,設計模式很重要。
關於中介軟體,有很多架構或者是類庫都使用了中介軟體,像express、koa、mongoose等都有使用。
2. Redux中介軟體的使用
我們可以使用Redux提供的applyMiddleware方法來使用一個或者是多個中介軟體,將它作為createStore的第二個參數傳入即可,我們以Redux-Thunk為例
import { createStore, applyMiddleware } from ‘redux‘import thunk from ‘redux-thunk‘const store = createStore(counter, applyMiddleware(thunk))ReactDOM.render( ( <Provider store={store}> <App /> </Provider> ), document.getElementById(‘root‘))
通過thunk中介軟體,我們就可以實現非同步action了。
export function addAsync(){ return dispatch => { setTimeout(() => { dispatch(add()) }, 2000); }}
想要實現中介軟體,我們首先有兩個任務要做:
擴充createStore方法,使它可以接收第二個參數。
applyMiddleware方法的實現。
3. createStore方法的擴充
我們在createStore中加入第二個參數enhancer, 專業的解釋應該叫增強器,叫middleware也可以的。
我們已經說過中介軟體的作用就是通過改變dispatch方法來改變資料流,所以我們這裡直接用enhancer對createStore方法進行裝飾。Redux的源碼也是這麼寫的,哈哈哈哈,怎麼和我想到的一模一樣呢?因為我看了Redux的源碼。。
export function createStore (reducer,enhancer) { if (enhancer) { return enhancer(createStore)(reducer) } let state = {} let listeners = [] function getState () { return state } function subscribe (listener) { listeners.push(listener) } function dispatch (action) { state = reducer(state, action) listeners.forEach(listener => listener()) return action } dispatch({type: ‘@myRedux‘}) return {getState, subscribe, dispatch}}
高階函數的寫法,應該都能看懂了吧?前幾篇隨筆有詳細的講高階函數,還有例子。
4.applyMiddleware方法的實現
先看我們上邊對enhancer的調用,enhancer也就是我們的applyMiddleware接受了createStore做參數,返回了一個函數,這個函數的參數是reducer。現在我們對這種兩層嵌套的函數已經不陌生了,其實它就是一個return兩層的函數。
我們的applyMiddleware主要做了什麼呢?首先通過傳入的createStore方法create了一個store,然後將store的dispatch傳遞給middleware,由middleware對dispatch進行封裝,返回一個帶有被封裝的dispatch的store。
看到這裡,很簡單嘛。但是注意,還記得我們是怎麼使用非同步action的嗎?
export function addAsync(){ return (dispatch, getState) => { setTimeout(() => { dispatch(add()) }, 2000); }}
居然還可以在可以在非同步action中拿到dispatch和getState方法,所以要對這個進行處理,也不是很難,把他倆傳給我們的middle就好了。
都說到這裡了,能不能自己寫出來呢?
export function applyMiddleware (middleware){ return createStore => (...args) => { const store = createStore(...args) let dispatch = store.dispatch const midApi = { getState: store.getState, dispatch: (...args)=>dispatch(...args) } dispatch = middleware(midApi)(store.dispatch) return { ...store, dispatch } }}
如果我們執行了被封裝後的dispatch,就相當於執行了middleware(midApi)(store.dispatch)(action)
這段語句,這是一個三層的嵌套函數,我們也稱作柯裡化。
5.自己的redux-thunk
其實自己的thunk很簡單,正常的action的的傳回值是個對象,前面已經說過,非同步action的傳回值是一個函數,那麼我們只需要判斷一下action的返回的類型即可。
const thunk = ({dispatch, getState}) => next => action => { if (typeof action === ‘function‘) { return action(dispatch, getState) } return next(action)}export thunk
在這裡呢,dispatch和getState就是我們在applyMiddleware中傳入的那個midApi對象,next就是store.dispatch也可以理解為下一個中介軟體,如果action的類型是object,說明這是一個同步的,直接dispatch就好了,如果
action的類型是function,當觸發這個dispatch的時候,就觸發action這個函數,同時將dispatch和getState方法傳入到action函數中,這也是為什麼我們能在非同步action中拿到dispatch和getState方法的原因。
6.多個中介軟體合并與compose方法
我們的applyMiddle方法還不是太完善,只能使用一個中介軟體,使用多個中介軟體怎麼辦,這個,簡單,map一下唄。如果是要求多個中介軟體依此執行怎麼辦?還是map呀,好,來map一下。
我們會得到這樣的代碼:
const store = createStore( reducer, applyMiddleware(middlewareOne) ( middlewareTwo( middlewareThree( ... ) ) ))
我們會發現,我們陷入了一個深度嵌套的函數當中,這時我們就需要一個compose方法來結合一下,方便我們的書寫。
compose是函數式編程的一種寫法,compose的作用是從右至左結合多個函數,形成一個最終函數。就是將fn1(fn2(fn3()))
的形式,變成compose(fn1, fn2, fn3)的形式。
compose 做的只是讓你在寫深度嵌套的函數時,避免了代碼的向右位移。不要覺得它很複雜。
compose方法的實現:
export function compose (...funcs){ if (funcs.length==0) { return arg=>arg } if (funcs.length==1) { return funcs[0] } return funcs.reduce((ret,item)=> (...args)=>{ console.log(ret) return ret(item(...args)) })}
compose不是那麼複雜,關於如果想瞭解更多關於compose的知識,可以看看Redux對compose的說明
到這裡我們可以使用多個中介軟體的applyMiddleware方法已經實現了,整個的applyMiddleware方法在這裡:
export function applyMiddleware (...middlewares){ return createStore=>(...args)=>{ const store = createStore(...args) let dispatch = store.dispatch const midApi = { getState:store.getState, dispatch:(...args)=>dispatch(...args) } const middlewareChain = middlewares.map(middleware=>{ return middleware(midApi) }) console.log(compose(...middlewareChain)(store.dispatch)) dispatch = compose(...middlewareChain)(store.dispatch) return { ...store, dispatch } }}export function compose(...funcs){ if (funcs.length==0) { return arg=>arg } if (funcs.length==1) { return funcs[0] } return funcs.reduce((ret,item)=> (...args)=>{ console.log(ret) return ret(item(...args)) })}
到這裡,整個Redux和React-Redux的基本原理我們已經清楚了,也已經基本實現了,發現其中涉及到很多函數式編程和裝飾者模式,還有一次觀察者模式,所以,編程思想和設計模式是很重要的,有時間一定要加強這方面的學習。
我們現在有了這些基礎,可以去看看Redux和React-Redux的源碼,也大體上和我寫的是差不多的,因為我也看了源碼。
Redux和React-Redux的實現(三):中介軟體的原理和applyMiddleware、Thunk的實現