淺談Redux中間件的實(shí)踐
最近項(xiàng)目前端開(kāi)發(fā)框架采用React+Redux進(jìn)行實(shí)現(xiàn),但是,如何異步訪問(wèn)服務(wù)器端,以及想要在開(kāi)發(fā)過(guò)程中進(jìn)行狀態(tài)樹(shù)日志的輸出,所以怎么才能解決這兩個(gè)問(wèn)題? 采用Redux中間件
為什么要使用中間件
在利用Redux進(jìn)行狀態(tài)管理時(shí),用戶在UI層面觸發(fā)行為,一個(gè)action對(duì)象通過(guò)store.dispatch派發(fā)到Reducer進(jìn)行觸發(fā),接下來(lái)Reducer會(huì)根據(jù)type來(lái)更新對(duì)應(yīng)的Store上的狀態(tài)樹(shù),更改后的state會(huì)觸發(fā)對(duì)應(yīng)組件的重新渲染。如下圖所示。在這個(gè)流程中,action對(duì)象是一個(gè)同步的對(duì)象,是一個(gè)包含type字段的簡(jiǎn)單對(duì)象,但是在訪問(wèn)服務(wù)器時(shí),由于瀏覽器是單線程的,不會(huì)一遍渲染組件一遍等待服務(wù)器返回的結(jié)果,因此我們需要設(shè)計(jì)一種異步訪問(wèn)服務(wù)器的方法來(lái)實(shí)現(xiàn)與服務(wù)器端的正常通信。
中間件介紹
在Redux架構(gòu)下,一個(gè)action對(duì)象在通過(guò)store.dispatch派發(fā),在調(diào)用reducer函數(shù)前,會(huì)先經(jīng)過(guò)一個(gè)中間件環(huán)節(jié),如下圖所示。
從上圖可以看出,在一個(gè)Redux架構(gòu)中可以用多個(gè)中間件,這些中間件一起組織處理請(qǐng)求的“管道”。一個(gè)中間件是一個(gè)獨(dú)立的函數(shù),可以組合使用,中間件有一個(gè)統(tǒng)一的接口,正因?yàn)橐粋€(gè)中間件只能完成一個(gè)特定的功能,所以把多個(gè)中間件組合在一起才能滿足比較豐富的應(yīng)用需求。當(dāng)然在使用時(shí),也需要按照順序依次處理傳入的action,只有排在前面的中間件完成任務(wù)之后,后面的中間件才能有機(jī)會(huì)繼續(xù)處理action。
中間件的特點(diǎn)
- 中間件是獨(dú)立的函數(shù)
- 中間件可以組合使用
- 中間件有一個(gè)統(tǒng)一的接口
中間件接口
每個(gè)中間件必須定義為一個(gè)函數(shù),返回一個(gè)接受next參數(shù)的函數(shù),而這個(gè)接受next參數(shù)的函數(shù)又返回一個(gè)接受action參數(shù)的函數(shù)。next參數(shù)本身也是一個(gè)函數(shù),中間件調(diào)用這個(gè)next函數(shù)通知Redux自己的處理工作已經(jīng)結(jié)束。
一個(gè)什么都不做的中間件代碼如下:
function doNothingMiddleware({dispatch, getState}) { return function(next){ return function(action){ return next(action); } } }
包含的功能有:
- 調(diào)用dispatch派發(fā)出一個(gè)新的action對(duì)象
- 調(diào)用getState獲得當(dāng)前Redux Store上的狀態(tài)
- 調(diào)用next告訴Redux當(dāng)前中間件工作完畢,讓Redux調(diào)用下一個(gè)中間件
- 訪問(wèn)action對(duì)象action上的所有數(shù)據(jù)
在一個(gè)Redux應(yīng)用如果想要使用中間件,必須通過(guò)applyMiddleware來(lái)生成。Redux的源碼文件非常簡(jiǎn)單,由五個(gè)文件一起組成,分別是createStore.js,applyMiddlware.js,compose.js,bindActionCreator.js,combineReducers.js。與createStore是用來(lái)創(chuàng)建一個(gè)狀態(tài)樹(shù),并且暴露出幾個(gè)方法,包括dispatch,subscribe,getState,replaceReducer和$$observable,給createStore傳入的參數(shù)有reducer,preloadedState和enhancer,其中enhancer就是一個(gè)store增強(qiáng)器,是一個(gè)函數(shù),只能用applyMiddleware生成。applyMiddleware函數(shù)是根據(jù)外部函數(shù)(中間件函數(shù))包裝原來(lái)的dispatch函數(shù),然后將新的dispatch函數(shù)暴露出去。
//根據(jù)外部函數(shù)(中間件函數(shù))包裝原來(lái)的dispatch函數(shù),然后將新的dispatch函數(shù)暴露了出去 export default function applyMiddleware(...middlewares) { //return一個(gè)函數(shù),它可以接受createStore方法作為參數(shù),給返回的store的dispatch方法再進(jìn)行一次包裝 return createStore => (...args) => {//agrs包含reducer, preloadedState, enhancer const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } //暴露兩個(gè)方法給外部函數(shù) const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } //傳入middlewareAPI參數(shù)并執(zhí)行每一個(gè)外部函數(shù),返回結(jié)果匯聚成數(shù)組 const chain = middlewares.map(middleware => middleware(middlewareAPI)) //這里用到了compose方法 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
中間件與增強(qiáng)器的區(qū)別
中間件和增強(qiáng)器都是對(duì)Redux Store的增強(qiáng),但是中間件僅僅是對(duì)Redux Store的dispatch方法進(jìn)行了增強(qiáng),也就是從dispatch函數(shù)調(diào)用到action對(duì)象被reducer處理這個(gè)過(guò)程中的操作,增強(qiáng)器是對(duì)Redux Store進(jìn)行更深層次的增強(qiáng)定制,需要使用Store Enhancer,通過(guò)閱讀增強(qiáng)器接口,一個(gè)增強(qiáng)器其實(shí)利用隨給的參數(shù)創(chuàng)造出一個(gè)store對(duì)象,然后定制對(duì)象,最后把Store對(duì)象返回。總的對(duì)比如下:
- 中間件: 可以用來(lái)增強(qiáng)redux store的dispatch函數(shù),也就是從dispatch函數(shù)調(diào)用到action對(duì)象被reducer處理這個(gè)過(guò)程中的操作
- 增強(qiáng)器: 對(duì)redux store進(jìn)行更深層次的增強(qiáng)定制,可以增強(qiáng)redux store的各個(gè)方面。
異步訪問(wèn)服務(wù)器:
異步action對(duì)象
在沒(méi)有引入中間件時(shí),社會(huì)治理子系統(tǒng)在開(kāi)發(fā)時(shí),所有的action都是同步的,一個(gè)同步的action對(duì)象是一個(gè)包含type字段的簡(jiǎn)單對(duì)象,但是我們需要實(shí)現(xiàn)一個(gè)異步action對(duì)象,是一個(gè)函數(shù),在action觸發(fā)之后,在reducer接收到執(zhí)行命令之前可以進(jìn)行一個(gè)異步操作。
我們引入redux-thunk來(lái)實(shí)現(xiàn)異步訪問(wèn)服務(wù)器方法,一個(gè)訪問(wèn)服務(wù)器的action,至少要涉及三個(gè)action類型:
- 表示異步操作已經(jīng)開(kāi)始的action類型;
- 表示異步操作成功的action類型;
- 表示異步操作失敗的action類型;
Redux-thunk源代碼解析
Redux-thunk中間件是Redux中異步操作的解決方法之一,在action對(duì)象被reducer函數(shù)處理之前,是插入異步功能的時(shí)機(jī),代碼非常簡(jiǎn)單:
function create ThunkMiddleware(extraArgument){ return ({dispatch, getState}) => next => action => { if(typeof action === ‘function'){ return action(dispatch, getState, extraArgument); } return next(action) } } const thunk = createThunkMiddleware(); export default thunk;
createThunkMiddleware函數(shù)返回了一個(gè)函數(shù),是實(shí)際處理每個(gè)action對(duì)象的函數(shù),首先檢查參數(shù)action的類型,如果是函數(shù)類型的話,就執(zhí)行這個(gè)action函數(shù),把dispatch和getState
作為參數(shù)傳遞出去,否則就調(diào)用next讓下一個(gè)中間件繼續(xù)處理action。
Redux-thunk的使用:
首先,安裝redux-thunk,在已經(jīng)安裝了node.js的命令窗口中運(yùn)行 “npm install redux-thunk --save-dev”,在store.js中引入redux-thunk,并且確保redux的applyMiddleware函數(shù)也引入。具體實(shí)現(xiàn)代碼如下。
import {createStore, combineReducers, applyMiddleware} from ‘redux'; import {otherState, dataState} from ‘reducers'; import thunkMiddleware from ‘redux-thunk'; var reducers = combineReducers({ otherState, dataState }); var store = createStore(reducers, applyMiddleware(thunkMiddleware)); export default store;
在成功引入了redux-thunk后,我們也要設(shè)計(jì)異步操作的action對(duì)象,例如,在設(shè)備管理模塊中,成功保存設(shè)備信息后要重新獲取設(shè)備信息,代碼如下:
function saveInfo(params){ let url = “/api/device”; return function(dispatch, getState){ dispatch(saveInfoRequest()); return Http.get(url, { params: params }).then(res=>{ if(res && res.type === 0){ dispatch(saveInfoSuccess ()); let dataState = getState().dataState; let newParams = { start: dataState.start, limit: dataState.limit, searchName: dataState.searchName }; dispatch(getInfo(newParams)) } }).catch(error=>{ dispatch(saveInfoFailure (error)); }); } }
從這個(gè)saveDeviceInfo返回的函數(shù)中,不僅可以dispatch一個(gè)同步的action對(duì)象,還可派發(fā)另一個(gè)異步action對(duì)象,來(lái)滿足一些有著先后關(guān)系的業(yè)務(wù)邏輯,代碼可讀性要比用Promise實(shí)現(xiàn)起來(lái)代碼更加清晰。
Redux-logger使用
在開(kāi)發(fā)階段,我們需要對(duì)redux數(shù)據(jù)流中每個(gè)流程進(jìn)行監(jiān)控,需要log輸出,redux-logger是官方推薦的一款日志中間件,使用起來(lái)非常方便。當(dāng)然,要使redux-logger生效,需要保證在系統(tǒng)中使用redux進(jìn)行狀態(tài)管理,否則沒(méi)有任何日志輸出。
Redux-logger的使用方法可以分為兩種,基本使用方法如下:
import { applyMiddleware, createStore} from ‘redux'; import logger from ‘reudx-logger' const store = createStore( Reducer, applyMiddleware(logger) )
也可以自己寫(xiě)一個(gè)日志輸出中間件,代碼如下:
var logger = store => next => action => { console.log('[action]', action) console.log(`[action] type:${action.type} payload:${JSON.stringify(action.payload)}`) next(action) console.log('[store]', store.getState()) console.log(`[store] ${JSON.stringify(store.getState())}`) }
總結(jié)
Redux中間件可以增強(qiáng)Store.dispatch方法,多個(gè)中間件可以組成“管道”,按照順序去處理action對(duì)象,在依次處理過(guò)后,才會(huì)有機(jī)會(huì)被reducer處理。中間件的應(yīng)用場(chǎng)景很多,除了可以支持異步訪問(wèn)服務(wù)器,還有許多很好的中間件插件,例如react-addons-perf進(jìn)行調(diào)試,和redux-logger來(lái)記錄狀態(tài),也可以根據(jù)業(yè)務(wù)需求來(lái)自己編寫(xiě)中間件,應(yīng)用非常靈活,在其他react項(xiàng)目中可以多加實(shí)踐。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于JS+Canves實(shí)現(xiàn)點(diǎn)擊按鈕水波紋效果
本文給大家分享基于js和canves實(shí)現(xiàn)點(diǎn)擊按鈕水波紋效果,效果非常逼真,對(duì)此感興趣的朋友一起看看吧2016-09-09深入理解JavaScript系列(27):設(shè)計(jì)模式之建造者模式詳解
這篇文章主要介紹了深入理解JavaScript系列(27):設(shè)計(jì)模式之建造者模式詳解,建造者模式可以將一個(gè)復(fù)雜對(duì)象的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示,需要的朋友可以參考下2015-03-03javascript中mouseenter與mouseover的異同
javascript中mouseover和mouseenter的區(qū)別主要在于監(jiān)聽(tīng)對(duì)象的子元素是否觸發(fā)事件。mouseover:鼠標(biāo)移入監(jiān)聽(tīng)對(duì)象中,或者從監(jiān)聽(tīng)對(duì)象的一個(gè)子元素移入另一個(gè)子元素中時(shí)觸發(fā)該事件。mouseenter:鼠標(biāo)移入監(jiān)聽(tīng)對(duì)象時(shí)觸發(fā),在監(jiān)聽(tīng)對(duì)象內(nèi)移動(dòng)不會(huì)觸發(fā)。2017-06-06JS函數(shù)進(jìn)階之繼承用法實(shí)例分析
這篇文章主要介紹了JS函數(shù)進(jìn)階之繼承用法,結(jié)合實(shí)例形式分析了JavaScript函數(shù)繼承相關(guān)定義與使用操作技巧,需要的朋友可以參考下2020-01-01javascript 三種方法實(shí)現(xiàn)獲得和設(shè)置以及移除元素屬性
獲得和設(shè)置以及移除元素屬性在操作dom的過(guò)程中會(huì)經(jīng)常遇到吧,為了提高工作的效率本文整理了一些快捷操作方法和大家一起分享,感興趣的朋友可以參考下哈2013-03-03使用JavaScript和CSS實(shí)現(xiàn)文本隔行換色的方法
這篇文章主要介紹了使用JavaScript和CSS實(shí)現(xiàn)文本隔行換色的方法,當(dāng)然最普通的也可以單純用CSS實(shí)現(xiàn),需要的朋友可以參考下2015-11-11Javascript實(shí)現(xiàn)登錄框拖拽效果
這篇文章主要為大家詳細(xì)介紹了Javascript實(shí)現(xiàn)登錄框拖拽效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10