[TOC]
## 什么是Redux中间件
![](https://box.kancloud.cn/6758d11e210483c91115d0f9cd74f62a_800x600.png)
中间件是插入在用户发射`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`剩下的代码。
![](https://box.kancloud.cn/e80cafbb47fa5a1adc36000b21505138_471x327.png)
执行结果会是:先输出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`其实就是我们的中间件
![](https://box.kancloud.cn/2cdd8e9b8d774fd94899f46033f2e064_988x581.png)
![](https://box.kancloud.cn/386567186ca5c62acece61d5027d4a7e_518x178.png)
## 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