这一小节会讲解 redux 中间件的原理,为下一节讲解 redux 异步 action 做铺垫,主要内容为:

  • Redux 中间件是什么

  • 使用 Redux 中间件

  • logger 中间件结构分析

  • applyMiddleware

  • 中间件的执行过程

3.3.1 Redux 中间件是什么

Redux moddleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.

redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截
action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。

3.3.2 使用 Redux 中间件

Redux 提供了一个叫 applyMiddleware 的方法,可以应用多个中间件,以日志输出中间件为例

import { createStore, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import rootReducer from './reducers'const loggerMiddleware = createLogger()
const initialState = {} return createStore(
rootReducer,
initialState,
applyMiddleware(
loggerMiddleware
)
)

3.3.3 logger 中间件结构分析

看看 redux-logger 的源码结构

function createLogger(options = {}) {
/**
* 传入 applyMiddleWare 的函数
* @param {Function} { getState }) [description]
* @return {[type]} [description]
*/return ({ getState }) => (next) => (action) => {
let returnedValue;
const logEntry = {};
logEntry.prevState = stateTransformer(getState());
logEntry.action = action;
// ....
returnedValue = next(action);
// ....
logEntry.nextState = stateTransformer(getState());
// ....return returnedValue;
};
} export default createLogger;

Logger 中这样的结构 ({ getState }) => (next) => (action) => {} 看起来是很奇怪的,这种设计如果没有 es6 的箭头函数,扩展下来就是

/**
* getState 可以返回最新的应用 store 数据
*/function ({getState}) {
/**
* next 表示执行后续的中间件,中间件有可能有多个
*/return function (next) {
/**
* 中间件处理函数,参数为当前执行的 action
*/return function (action) {...}
}
}

这样的结构本质上就是为了将 middleware 串联起来执行,为了分析 middleware 的执行顺序,还得看看 applyMiddleware 的实现

3.3.4 applyMiddleware 分析

下面是 applyMiddleware 完整的代码,参数为 middlewares 数组:

import compose from './compose'/**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as expressing
* asynchronous actions in a concise manner, or logging every action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState` functions
* as named arguments.
*
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
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
}
}
}
  1. applyMiddleware 执行过后返回一个闭包函数,目的是将创建 store的步骤放在这个闭包内执行,这样 middleware 就可以共享 store 对象。

  2. middlewares 数组 map 为新的 middlewares 数组,包含了 middlewareAPI

  3. compose 方法将新的 middlewares 和 store.dispatch 结合起来,生成一个新的 dispatch 方法

  4. 返回的 store 新增了一个 dispatch 方法, 这个新的 dispatch 方法是改装过的 dispatch,也就是封装了中间件的执行。

所以关键点来到了 compose 方法了,下面来看一下 compose 的设计:

export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} if (funcs.length === 1) {
return funcs[0]
} const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

可以看到 compose 方法实际上就是利用了 Array.prototype.reduceRight 。如果对 reduceRight 不是很熟悉,来看看下面的一个例子就清晰了:

/**
* [description]
* @param {[type]} previousValue [前一个项]
* @param {[type]} currentValue [当前项]
*/
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
return previousValue + currentValue;
}, 10);

执行结果:

# previousValue currentValue return value
第一次 10 4 14
第二次 14 3 17
第三次 17 2 19
第四次 19 1 20
第五次 20 0 20

3.3.5 理解中间件的执行过程

通过上面的 applyMiddleware 和 中间件的结构,假设应用了如下的中间件: [A, B, C],一个 action 的完整执行流程

初始化阶段

一个中间件的结构为

function ({getState}) {
return function (next) {
return function (action) {...}
}
}

初始化阶段一:middlewares map 为新的 middlewares

chain = middlewares.map(middleware => middleware(middlewareAPI))

执行过后,middleware 变为了

function (next) {
return function (action) {...}
}

初始化阶段二:compose 新的 dispatch

const newDispatch = compose(newMiddlewares)(store.dispatch)

dispatch 的实现为 reduceRight, 当一个新的 action 来了过后

/**
* 1. 初始值为: lastMiddleware(store.dispatch)
* 2. previousValue: composed
* 3. currentValue: currentMiddleware
* 4. return value: currentMiddleware(composed) => newComposed
*/
rest.reduceRight((composed, f) => f(composed), last(...args))

composed 流程

reduceRight 的执行过程:

初始时候

  1. initialValue: composedC = C(store.dispatch) = function C(action) {}

  2. next 闭包: store.dispatch

第一次执行:

  1. previousValue(composed): composedC

  2. currentValue(f): B

  3. return value: composedBC = B(composedC) = function B(action){}

  4. next 闭包 composedC

第二次执行:

  1. previousValue(composed): composedBC

  2. currentValue(f): A

  3. return value: composedABC = A(composedBC) = function A(action){}

  4. next 闭包 composedBC

最后的返回结果为 composedABC

执行阶段

  1. dispatch(action) 等于 composedABC(action) 等于执行 function A(action) {...}

  2. 在函数 A 中执行 next(action), 此时 A 中 next 为 composedBC,那么等于执行 composedBC(action) 等于执行 function B(action){...}

  3. 在函数 B 中执行 next(action), 此时 B 中 next 为 composedC,那么等于执行 composedC(action) 等于执行 function C(action){...}

  4. 在函数 C 中执行 next(action), 此时 C 中 next 为 store.dispatch 即 store 原生的 dispatch, 等于执行store.dispatch(action)

  5. store.dispatch 会执行 reducer 生成最新的 store 数据

  6. 所有的 next 执行完过后开始回溯

  7. 执行函数 C 中 next 后的代码

  8. 执行函数 B 中 next 后的代码

  9. 执行函数 A 中 next 后的代码

整个执行 action 的过程为 A -> B -> C -> dispatch -> C -> B -> A

3.3 理解 Redux 中间件(转)的更多相关文章

  1. 理解 Redux 中间件机制

    Redux 的 action 是一个 JS 对象,它表明了如何对 store 进行修改.但是 Redux 的中间件机制使action creator 不光可以返回 action 对象,也可以返回 ac ...

  2. redux深入理解之中间件(middleware)

    理解reduce函数 reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值. arr.reduce([callback, initi ...

  3. 理解 Redux 的中间件

    将该思想抽象出来,其实和 Redux 就无关了.问题变成,怎样实现在截获函数的执行,以在其执行前后添加自己的逻辑. 为了演示,我们准备如下的示例代码来模拟 Redux dispatch action ...

  4. 理解Redux以及如何在项目中的使用

    今天我们来聊聊Redux,这篇文章是一个进阶的文章,建议大家先对redux的基础有一定的了解,在这里给大家推荐一下阮一峰老师的文章: http://www.ruanyifeng.com/blog/20 ...

  5. 通俗易懂的理解 Redux(知乎)

    1. React有props和state: props意味着父级分发下来的属性[父组件的state传递给子组件  子组件使用props获取],state意味着组件内部可以自行管理的状态,并且整个Rea ...

  6. Redux:中间件

    redux中间件概念 比较容易理解. 在使用redux时,改变store state的一个固定套路是调用store.dispatch(action)方法,将action送到reducer中. 所谓中间 ...

  7. react+redux教程(七)自定义redux中间件

    今天,我们要讲解的是自定义redux中间件这个知识点.本节内容非常抽象,特别是中间件的定义原理,那多层的函数嵌套和串联,需要极强逻辑思维能力才能完全消化吸收.不过我会多罗嗦几句,所以不用担心. 例子 ...

  8. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  9. 【React全家桶入门之十三】Redux中间件与异步action

    在上一篇中我们了解到,更新Redux中状态的流程是这种:action -> reducer -> new state. 文中也讲到.action是一个普通的javascript对象.red ...

随机推荐

  1. IOS 创建目录/文件夹

    •IOS 应用目录简介 个文件夹:Documents, Library和 tmp.Library包含Caches.Preferences目录. Documents:应用中用户数据可以放在这里,iTun ...

  2. 90、Tensorflow实现分布式学习,多台电脑,多个GPU 异步试学习

    ''' Created on 2017年5月28日 @author: weizhen ''' import time import tensorflow as tf from tensorflow.e ...

  3. mybatis之增删改

    前面三小节内容主要是针对查询操作进行讲解,现在对mybatis增删改进行演示. 由于每次建立工程比较复杂,可以参考第一节:mybatis入门来搭建一个简单的工程,然后来测试本节内容. 1.增 1.新增 ...

  4. 分布式-技术专区-Redis分布式锁实现-第二步

    再上次篇章中汇集了相关的分布式锁的概念进行控制,接下来我们采用的是注解声明式开发服务方案,进行声明式开发代替编程式开发方案.  1.利用aop实现分布式锁2.只用在方法上加个注解,同时加上了重试机制 ...

  5. Python之OS(系统操作)模块常用函数

    mkdir(path[, mode=0777]) makedirs(name,mode=511) rmdir(path) removedirs(path) listdir(path) getcwd() ...

  6. Python值正则表达式(RE)

    要想在Python中使用正则表达式,首先要引入模块: import re . 匹配任意一个 +   匹配至少一个 * 匹配0个至多个 ? 1个或0个(可有可无) - 表范围 \ 转义 ^   在首 $ ...

  7. mysql8的坑

    plugin caching_sha2_password could not be loaded 我在mac上用Sequel Pro连数据库的时候,会报出以上错误,这是应为8.0.11把身份认证插件改 ...

  8. python--函数基础一

    一.函数意义:可读性,复用性强 # def my_len(s): #def 关键词开头,my_len函数名 # i=0 # for k in s: # i+=1 # print(i) # return ...

  9. Docker部署web环境之Lanmt

    2. 案例二 整套项目多容器分离通过docker-compose部署lanmt环境 详细的安装准备环境,省略,配置以及部署参考案例一即可 即可实现批量创建web,也可以实现多web共用一个php或my ...

  10. 【CSS】div的背景图完整图片覆盖

    最初的代码: .container_first { width: 100%; height: 100%; background: url(10176581.jpg); background-size: ...