[TOC]
## 什么是Redux中間件

中間件是插入在用戶發射`action`動作之后到`reducer`接收到這個動作之前這個時機的處理器,它能完成一些額外的邏輯。
## 存在中間件時倉庫的初始化
平時我們是這么初始化一個有中間件的倉庫的
```
import {createStore,applyMiddleware} from 'redux';
let store = createStore(reducer,{astate:..,bstate:...,...},applyMiddleware(xx,yy,zz));
```
其實內部是這樣調用的
```
let store = applyMiddleware(xxx,yyy,...)(createStore)(reducer,{astate:..,bstate:...,...})
```
emmm,原理是這樣的
```
export default function createStore(reducer, preloadedState, enhancer){ // 增強器
if(enhancer && typeof enhancer === 'function'){
return enhancer(createStore)(reducer,preloadedState)
}
...
```
So,我們現在知道了,如果有中間件,會先執行`applyMiddleware`應用中間件這個方法,并且將`createStore`、`reducer`、`preloadedState`傳到applyMiddleware這個方法里面,在里面初始化倉庫。
## 創建一個中間件
我們再來看看平時我們是怎么定義一個中間件的
```
let logger = ({dispatch,getState})=>next=>action=>{
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
```
它等價于
```
let logger = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
}
}
```
其中,`getState` 就是`createStore`初始化一個倉庫時原本導出的getState,`action`也還是用戶所發射的那個reducer所接受的那個action,
但`dispatch`不再是createStore所導出的那個了,而是經過中間件包裝后的dispatch。
另外`next`其實是另外一個中間件,和Koa中的中間件一樣我們需要手動調用next來讓隊列中的中間件一次執行。并且中間件的運行及結果依然遵循**洋蔥模型**。
我們來看這樣一個栗子,假若我們有兩個中間件
```
//中間件1
let middle1 = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
}
}
//中間件2
let middle2 = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(2);
next(action);
console.log(22);
//do some thing else agin
}
}
}
```
然后我們這樣初始化倉庫時這樣注冊他們
```
let store = applyMiddleware(middle2,middle1)(createStore)(reducer);
```
那么當我們發射一個動作時,比如說
```
<button onClick={this.props.increment}>+</button>
```
那么此時就會調用派發,只不過此時的派發方法是經過先經過`middle1`包裝后又經過`middle2`包裝的方法。它會先執行`middler2`額外添加的一部分代碼,然后再執行`middle1`額外添加的一部分代碼,然后才會真正的派發,派發完以后又會將`middle1`剩下的代碼執行完,最后是`middle2`剩下的代碼。

執行結果會是:先輸出1,再輸出2,再執行store.dispatch,再輸出22,最后輸出11
接下來,我們來看這樣的模型具體是怎樣實現的。
## applyMiddleware.js
這個方法最后會將原本的`store.dispatch`給替換成**新的dispatch**,這個新的dispatch是則是我們上面所說的經過中間件層層包裝后的新的dispatch。
```
//applyMiddleware.js
export default function(...middlewares){
return function(createStore){
return function(reducer,preloadedState){
let store = createStore(reducer,preloadedState);
let dispatch;
let middlewareAPI = {
getState:store.getState
,dispatch:action=>dispatch(action)
}
middlewares = middlewares.map(middleware(middlewareAPI));
dispatch = compose(...middlewares)(store.dispatch);
return {...store,dispatch};
}
}
}
```
## compose.js
在`applyMiddleware.js`中我們用到了一個`compose`方法,
這個方法會把中間件串行起來,并且一層包一層,按照`applyMiddleware()`調用時中間件注冊的順序,**先注冊的會成為洋蔥模型最外面的一層**,后注冊的則往里一層,最里面是原本的store.dispatch。
```
//compose.js
export default function(...fns){
return function(...args){
let last = fns.pop();
return fns.reduceRight((val,fn)=>{
return fn(val);
},last(...args));
}
}
//--- --- ---
//高逼格版
export default function(...fns){
if(fns.length === 1)return fns[0];
return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
```
為了便于理解`compose`函數的作用,請看一下以下的例子
```
function add1(str){
return str+'com';
}
function add2(str){
return str+'po';
}
function add3(str){
return str+'se!';
}
add3(add2(add1('hello,'))); //輸出:hello,compose!
//以上等價于
let add = compose(add3,add2,add1);
add('hello,');
```
So,上面的`add`其實就是我們的中間件


## thunk
```
...
,thunkIncrement(){
return function(dispatch,getState){
setTimeout(function(){
dispatch({type:types.INCREMENT,payload:1})
},1000);
}
}
...
```
```
//處理自定義異步操作的中間件
let thunk = ({dispatch,getState})=>next=>action=>{ //處理函數的
if(typeof action == 'function'){
action(dispatch,getState); //說明是一個異步動作,將dispatch等傳給這個異步動作的函數,在異步有結果后再執行派發改變原本狀態。
}else{
next(action); //匹配下一個中間件
}
};
```
## promise
```
...
//1)
,promiseIncrement(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve({type:types.INCREMENT,payload:1}); //reject無法處理
},1000)
});
}
//2)
,payloadIncrement(){
return {
type:types.INCREMENT
,payload:new Promise(function(resolve,reject){
setTimeout(function(){
if(Math.random()>.5){
resolve(100);
}else{
reject(-100);
}
},1000)
})
}
}
...
```
```
//處理promise的中間件
let promise = ({dispatch,getState})=>next=>action=>{
if(action.then&&typeof action.then === 'function'){ //是一個promise
action.then(dispatch);
}else if(action.payload&&action.payload.then){
action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
} else{
next(action);
}
};
```
- 空白目錄
- 01.JSX,了解一下?
- JSX與虛擬DOM
- React
- 02.React文檔精讀(上)`
- React路由
- 關于BrowserRouter
- 關于Route
- 應用
- 權限認證
- case1
- context
- 新context
- 03.React路由
- 04.Diff
- 05.styled-components
- redux設計思想與API
- redux實現1
- 06.redux2
- 06.redux3
- 關于狀態初始化
- saga
- 新版
- 使用saga進行業務邏輯開發
- react-router-redux
- React性能優化
- immutable使用
- 未整理
- FAQ
- 常用中間件
- pureComponent
- 項目相關總結
- antd分尸
- 按需加載
- ReactWithoutJSX
- 我的組件庫
- C領域
- 用戶接口
- htmlType
- style
- show
- conjure
- grid
- inject
- stop
- 內部接口
- 衍生組件
- Button
- 報錯集錦
- ReactAPI
- 類上的那些屬性
- prop-types
- React.createElement
- React.cloneElement
- React.Children和props.children
- react元素和react組件關于作為children方面的那些問題
- react組件與虛擬dom
- ref