一烘跺、中間件的概念
redux
是有流程的,那么镶蹋,我們該把這個異步操作放在哪個環(huán)節(jié)比較合適呢?
-
Reducer
?純函數(shù)只承擔計算State
功能赏半,不適合其它功能贺归。 -
View
?與State
一一對應,可以看做是State
的視覺層断箫,也不適合承擔其它功能拂酣。 -
Action
?它是一個對象仲义,即存儲動作的載體婶熬,只能被操作。
其實埃撵,也只有dispatch
能勝任此重任了赵颅。那么怎么在dispatch
中添加其它操作呢?
let next = store.dispatch;
store.dispatch = function(action){
console.log('老狀態(tài) ',store.getState());
next(action);
console.log('新狀態(tài) ',store.getState());
}
復制代碼
示例中可以看出暂刘,我們對store.dispatch
重新進行了定義饺谬,在發(fā)送action
的前后,做了打印鸳惯。
這是中間件的大致雛形商蕴,真實的中間件要比這么復雜多了
二、中間件的用法
我們在這里先看看中間件是怎么使用芝发,下面我們一步步剖析每個細節(jié)绪商。
import {applyMiddleware,createStore} from 'redux';
import reduxLogger form 'redux-logger';
const store = createStore(reducer,inital_state,applyMiddleware(thunk, promise,reduxLogger));
復制代碼
代碼中有兩點需要注意:
- 1、
createStore
方法可以整個應用的初始狀態(tài)作為參數(shù) 內(nèi)部是這么處理的
let state = inital_state;
復制代碼
- 2辅鲸、中間件的參數(shù)次序有講究格郁。下面我會把這個問題講明白。
三、applyMiddleware
Middleware可以讓你包裝store
的dispatch
方法來達到你想要的目的例书。同時锣尉,middleWare
還擁有“可組合”這一關鍵特性。多個middleWare
可以被組合到一起使用决采,形成middleWare
鏈自沧,依次執(zhí)行。其中每個middleware
不需要關心鏈前后的的middleWare
的任何信息树瞭。
function applyMiddleware(...middlewares){
return function(createStore){
return function(reducer){
//引入store
let store = createStore(reducer);
let dispatch = store.dispatch;
let middlewareAPI = {
getState:store.getState,
// 對dispatch進行包裝
dispatch:action=>dispatch(action)
}
//每個中間件都是這種模型 ({ getState, dispatch }) => next => action
chain = middlewares.map(middleware=>middleware(middleAPI));
dispatch = compose(...chain)(store.dispatch);
// dispatch被改裝后拇厢,返回store
return{...store,dispatch};
}
}
}
復制代碼
上面代碼中,所有中間件都被放進了一個數(shù)組chain
,然后嵌套執(zhí)行晒喷,最后執(zhí)行store.dispatch
孝偎。中間件內(nèi)部middlewaAPI
可以拿到getState
和dispatch
這兩個方法。
...middleware
:遵循Redux middleware API
的函數(shù)凉敲。每個middleware
接受Store
的dispatch
和getState
函數(shù)作為命名參數(shù)衣盾,并返回一個函數(shù)。該函數(shù)會被傳入成為next
的下一個middleWare 的dispatch方法爷抓,并返回一個接收action的新函數(shù)势决,這個函數(shù)可以直接調(diào)用next(action),或者在其他需要的時刻調(diào)用废赞,甚至根本不去調(diào)用它徽龟。
所以,接下來唉地,我們就能看到middleware的函數(shù)簽名是({ getState, dispatch }) => next => action
其實据悔,它的本質(zhì)就是包裝sotre中的dispatch
。
上面代碼中耘沼,還用到了compose
方法极颓,我們來看看compose是怎么是實現(xiàn)的?
compose
先看下面一個栗子:
function add1(str){
return str+1;
}
function add2(str){
return str+2;
}
function add3(str){
return str+3;
}
let result = add3(add2(add1('好吃')));// 好吃123;
復制代碼
這中寫法調(diào)用起來群嗤,一層套一層菠隆,是不是看著很不爽,我們簡化一下:
function compose(...fns){
if(fns.length==1)
return fns[0];
return function(...args){
let last = fns.pop();
return fns.reduceRight((prev,next)=>{
return next(prev);
},last(...args));
}
}
let add = compose(add3,add2,add1);//
let result = add('好吃');// 好吃123
// 上面的代碼其實就是redux3.6.0版本中compose的實現(xiàn)方式
復制代碼
看看這個代碼是不是用起來狂秘,很干練一些骇径。其實還可以簡化
function compose(...fns){
if(fns.length==1)
return fns[0];
return fns.reduce((a,b)=>(...args)=>a(b(...args)));//add3(add2(add1('好吃')))
}
let add = compose(add3,add2,add1);//
let result = add('好吃');// 好吃123
// 這是redux3.6.0版本之后的compose實現(xiàn)方式,一直沿用至今者春。
復制代碼
至于為什么applyMiddleWare
的參數(shù)有順序破衔,這里給出了答案。
四钱烟、Applymiddleware的三個常用參數(shù)
4.1晰筛、日志記錄
使用 Redux 的一個益處就是它讓 state 的變化過程變的可預知和透明嫡丙。每當一個 action 發(fā)起完成后,新的 state 就會被計算并保存下來读第。State 不能被自身修改曙博,只能由特定的 action 引起變化。
試想一下怜瞒,當我們的應用中每一個 action 被發(fā)起以及每次新的 state 被計算完成時都將它們記錄下來父泳,豈不是很好?當程序出現(xiàn)問題時吴汪,我們可以通過查閱日志找出是哪個 action 導致了 state 不正確尘吗。
圖片的效果是不是很期待啊=阶!黔宛!
我們先來手動實現(xiàn)一版近刘。
// 記錄所有被發(fā)起的action和新的state
let next = store.dispatch;
store.dispatch = function(action){
console.log('老狀態(tài) ',store.getState());
next(action);
console.log('新狀態(tài) ',store.getState());
}
復制代碼
還是上面的示例,我們來做個修改
let logger = function({ getState, dispatch }){
return function(next){// 這里的next可以理解為store.dispath,本質(zhì)上就是調(diào)用 middleware 鏈中下一個 middleware 的 dispatch臀晃。
return function(action){
console.log('老狀態(tài)1 ',getState());
next(action);//派發(fā)動作
console.log('新狀態(tài)1 ',getState());
}
}
}
// 高逼格寫法
let logger = ({ getState, dispatch }) => next => action => {
console.log('老狀態(tài)1 ',getState());
next(action)
console.log('新狀態(tài)1 ',getState());
}
復制代碼
4.2觉渴、redux-thunk 中間件
redux-thunk
是redux
官方文檔中用到的異步組件,實質(zhì)就是一個redux
中間件徽惋,一個封裝表達式的函數(shù)案淋,封裝的目的就是延遲執(zhí)行表達式。
redux-thunk
是一個通用的解決方案险绘,其核心思想是讓action
可以變成一個thunk
踢京,這樣的話,同步情況:dispatch(action)
,異步情況:dispatch(thunk)
宦棺。
下面是redux-thunk
的實現(xiàn):
let thunk = ({dispatch,getState})=>next=>action=>{
if(typeof action == 'function'){
action(dispatch,getState);
}else{
next(action);//這里可以理解為dispatch(action),本質(zhì)上就是調(diào)用 middleware 鏈中下一個 middleware 的 dispatch瓣距。
}
}
復制代碼
使用redux-thunk
const store = createStore(
reducer,
applyMiddleware(thunk)
);
復制代碼
然后我們實現(xiàn)一個thunkActionCreator
//過一秒加1
export function thunkActionCreator(payload){
return function(dispatch,getState){
setTimeout(function(){
dispatch({type:types.INCREMENT,payload:payload});
},1000);
}
},
復制代碼
最后,在組件中dispatch thunk
this.dispatch(thunkActionCreator(payload));
復制代碼
4.3代咸、redux-promise
redux-promise
也是延遲執(zhí)行的表達式蹈丸,它是解決異步的另外一種方案。
redux-thunk
和核心思想是把action
變成thunk
呐芥,而redux-promise
的核心思想是讓action
返回一個promise對象逻杖。
這個中間件使得store.dispatch
方法可以接收Promise對象作為參數(shù)。這時 思瘟,action 有兩種寫法:
寫法一荸百、返回值是一個Promise對象。
function promiseIncrement(payload){
// return {type:types.INCREMENT,payload:payload} 以前是這種寫法
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve({type:types.INCREMENT,payload:payload});
},1000);
});
},
復制代碼
寫法二潮太,action 對象的payload屬性是一個Promise對象管搪,這需要從
function payloadIncrement(){
return {
type:types.INCREMENT,
payload: new Promise(function(resolve,reject){
setTimeout(function(){
if(Math.random()>.5){
resolve(100);
}else{
reject(-100);
}
},1000)
})
}
}
復制代碼
下面我們來看看 redux-promise
是怎么實現(xiàn)的虾攻,就會明白它內(nèi)部是怎么操作的.
let promise = ({dispatch,getState})=>next=>action=>{
if(action.then && typeof action.then == 'function'){
action.then(dispatch);
// 這里的dispatch就是一個函數(shù),dispatch(action){state:reducer(state,action)};
}else if(action.payload&& action.payload.then&& typeof action.payload.then == 'function'){
action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
}else{
next(action);
}
}
復制代碼
上面的代碼可以看出更鲁,如果Action本身就是一個Promise霎箍,它resolve以后的值應該是一個Action對象,會被dispatch方法送出action.then(dispatch)澡为;如果Action
對象的 payload
屬性是一個Promise
對象漂坏,那么無論resolve
和reject
,dispatch 方法都會發(fā)出Action
。
需要dubbo視頻教程的朋友媒至。加QQ群:957734884領取資料顶别。