說(shuō)說(shuō)面試官:說(shuō)說(shuō)對(duì)Redux中間件的理解?常用的中間件有哪些?實(shí)現(xiàn)原理?
一、是什么
中間件(Middleware)在計(jì)算機(jī)中,是介于應(yīng)用系統(tǒng)和系統(tǒng)軟件之間的一類軟件,它使用系統(tǒng)軟件所提供的基礎(chǔ)服務(wù)(功能),銜接網(wǎng)絡(luò)上應(yīng)用系統(tǒng)的各個(gè)部分或不同的應(yīng)用,能夠達(dá)到資源共享、功能共享的目的
在這篇文章中,了解到了Redux整個(gè)工作流程,當(dāng)action發(fā)出之后,reducer立即算出state,整個(gè)過(guò)程是一個(gè)同步的操作
那么如果需要支持異步操作,或者支持錯(cuò)誤處理、日志監(jiān)控,這個(gè)過(guò)程就可以用上中間件
Redux中,中間件就是放在就是在dispatch過(guò)程,在分發(fā)action進(jìn)行攔截處理,如下圖:
其本質(zhì)上一個(gè)函數(shù),對(duì)store.dispatch方法進(jìn)行了改造,在發(fā)出 Action和執(zhí)行 Reducer這兩步之間,添加了其他功能
二、常用的中間件
有很多優(yōu)秀的redux中間件,這里我們例舉兩個(gè):
- redux-thunk:用于異步操作
- redux-logger:用于日志記錄
上述的中間件都需要通過(guò)applyMiddlewares進(jìn)行注冊(cè),作用是將所有的中間件組成一個(gè)數(shù)組,依次執(zhí)行
然后作為第二個(gè)參數(shù)傳入到createStore中
- const store = createStore(
- reducer,
- applyMiddleware(thunk, logger)
- );
redux-thunk
redux-thunk是官網(wǎng)推薦的異步處理中間件
默認(rèn)情況下的dispatch(action),action需要是一個(gè)JavaScript的對(duì)象
redux-thunk中間件會(huì)判斷你當(dāng)前傳進(jìn)來(lái)的數(shù)據(jù)類型,如果是一個(gè)函數(shù),將會(huì)給函數(shù)傳入?yún)?shù)值(dispatch,getState)
dispatch函數(shù)用于我們之后再次派發(fā)action
getState函數(shù)考慮到我們之后的一些操作需要依賴原來(lái)的狀態(tài),用于讓我們可以獲取之前的一些狀態(tài)
所以dispatch可以寫(xiě)成下述函數(shù)的形式:
- const getHomeMultidataAction = () => {
- return (dispatch) => {
- axios.get("http://xxx.xx.xx.xx/test").then(res => {
- const data = res.data.data;
- dispatch(changeBannersAction(data.banner.list));
- dispatch(changeRecommendsAction(data.recommend.list));
- })
- }
- }
redux-logger
如果想要實(shí)現(xiàn)一個(gè)日志功能,則可以使用現(xiàn)成的redux-logger
- import { applyMiddleware, createStore } from 'redux';
- import createLogger from 'redux-logger';
- const logger = createLogger();
- const store = createStore(
- reducer,
- applyMiddleware(logger)
- );
這樣我們就能簡(jiǎn)單通過(guò)中間件函數(shù)實(shí)現(xiàn)日志記錄的信息
三、實(shí)現(xiàn)原理
首先看看applyMiddlewares的源碼
- export default function applyMiddleware(...middlewares) {
- return (createStore) => (reducer, preloadedState, enhancer) => {
- var store = createStore(reducer, preloadedState, 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}
- }
- }
所有中間件被放進(jìn)了一個(gè)數(shù)組chain,然后嵌套執(zhí)行,最后執(zhí)行store.dispatch??梢钥吹?,中間件內(nèi)部(middlewareAPI)可以拿到getState和dispatch這兩個(gè)方法
在上面的學(xué)習(xí)中,我們了解到了redux-thunk的基本使用
內(nèi)部會(huì)將dispatch進(jìn)行一個(gè)判斷,然后執(zhí)行對(duì)應(yīng)操作,原理如下:
- function patchThunk(store) {
- let next = store.dispatch;
- function dispatchAndThunk(action) {
- if (typeof action === "function") {
- action(store.dispatch, store.getState);
- } else {
- next(action);
- }
- }
- store.dispatch = dispatchAndThunk;
- }
實(shí)現(xiàn)一個(gè)日志輸出的原理也非常簡(jiǎn)單,如下:
- let next = store.dispatch;
- function dispatchAndLog(action) {
- console.log("dispatching:", addAction(10));
- next(addAction(5));
- console.log("新的state:", store.getState());
- }
- store.dispatch = dispatchAndLog;
參考文獻(xiàn)
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html
http://cn.redux.js.org/