3.3 理解 Redux 中间件(转)
这一小节会讲解 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
}
}
}
applyMiddleware执行过后返回一个闭包函数,目的是将创建store的步骤放在这个闭包内执行,这样middleware就可以共享store对象。middlewares数组 map 为新的middlewares数组,包含了middlewareAPIcompose方法将新的middlewares和store.dispatch结合起来,生成一个新的dispatch方法返回的
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 的执行过程:
初始时候
initialValue: composedC = C(store.dispatch) = function C(action) {}
next 闭包: store.dispatch
第一次执行:
previousValue(composed): composedC
currentValue(f): B
return value: composedBC = B(composedC) = function B(action){}
next 闭包 composedC
第二次执行:
previousValue(composed): composedBC
currentValue(f): A
return value: composedABC = A(composedBC) = function A(action){}
next 闭包 composedBC
最后的返回结果为 composedABC
执行阶段
dispatch(action)等于composedABC(action)等于执行function A(action) {...}在函数 A 中执行
next(action), 此时 A 中next为composedBC,那么等于执行composedBC(action)等于执行function B(action){...}在函数 B 中执行
next(action), 此时 B 中next为composedC,那么等于执行composedC(action)等于执行function C(action){...}在函数 C 中执行
next(action), 此时 C 中next为store.dispatch即 store 原生的 dispatch, 等于执行store.dispatch(action)store.dispatch 会执行 reducer 生成最新的 store 数据
所有的 next 执行完过后开始回溯
执行函数 C 中 next 后的代码
执行函数 B 中 next 后的代码
执行函数 A 中 next 后的代码
整个执行 action 的过程为 A -> B -> C -> dispatch -> C -> B -> A
3.3 理解 Redux 中间件(转)的更多相关文章
- 理解 Redux 中间件机制
Redux 的 action 是一个 JS 对象,它表明了如何对 store 进行修改.但是 Redux 的中间件机制使action creator 不光可以返回 action 对象,也可以返回 ac ...
- redux深入理解之中间件(middleware)
理解reduce函数 reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值. arr.reduce([callback, initi ...
- 理解 Redux 的中间件
将该思想抽象出来,其实和 Redux 就无关了.问题变成,怎样实现在截获函数的执行,以在其执行前后添加自己的逻辑. 为了演示,我们准备如下的示例代码来模拟 Redux dispatch action ...
- 理解Redux以及如何在项目中的使用
今天我们来聊聊Redux,这篇文章是一个进阶的文章,建议大家先对redux的基础有一定的了解,在这里给大家推荐一下阮一峰老师的文章: http://www.ruanyifeng.com/blog/20 ...
- 通俗易懂的理解 Redux(知乎)
1. React有props和state: props意味着父级分发下来的属性[父组件的state传递给子组件 子组件使用props获取],state意味着组件内部可以自行管理的状态,并且整个Rea ...
- Redux:中间件
redux中间件概念 比较容易理解. 在使用redux时,改变store state的一个固定套路是调用store.dispatch(action)方法,将action送到reducer中. 所谓中间 ...
- react+redux教程(七)自定义redux中间件
今天,我们要讲解的是自定义redux中间件这个知识点.本节内容非常抽象,特别是中间件的定义原理,那多层的函数嵌套和串联,需要极强逻辑思维能力才能完全消化吸收.不过我会多罗嗦几句,所以不用担心. 例子 ...
- 轻松理解Redux原理及工作流程
轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...
- 【React全家桶入门之十三】Redux中间件与异步action
在上一篇中我们了解到,更新Redux中状态的流程是这种:action -> reducer -> new state. 文中也讲到.action是一个普通的javascript对象.red ...
随机推荐
- 使用Intent实现Activity之间传值与跳转(转)
转:http://blog.csdn.net/cjjky/article/details/6337447 在一个Android的应用程序中,很少只存在一个Activity,一般都有多个Activity ...
- Linux命令 who
who :显示当前登入系统的用户信息 显示的内容主要包括: 用户名,登录终端,上线时间,停留时间,动作,UID等 权限:所有使用者 语法: who [option] ...[ file | arg1 ...
- 用iptables实现代理上网
环境:内网:eth1:192.168.2.0/24外网:eth0:10.17.0.111用iptables实现NATSNAT:改变数据包的源地址.防火墙会使用外部地址,替换数据包的本地网络地址.这样使 ...
- 【C++第一个Demo】---控制台RPG游戏4【角色系统】
[角色基类] #ifndef _ROLE_H_ #define _ROLE_H_ #include<list> #include<vector> #include " ...
- Java DOM解析器 - 解析XML文档
使用DOM的步骤 以下是在使用DOM解析器解析文档使用的步骤. 导入XML相关的软件包. 创建DocumentBuilder 从文件或流创建一个文档 提取根元素 检查属性 检查子元素 导入XML相关的 ...
- Python系列——常用第三方库
幕布视图(更加方便.明了):https://mubu.com/doc/AqoVZ8x6m0 参考文献:嵩天老师的Python讲义 模块 定义 计算机在开发过程中,代码越写越多,也就越难以维护,所以为了 ...
- 12-vim-撤销和删除命令-02-删除文本
删除文本 命令 英文 功能 x cut 删除光标所在字符 d(移动命令) delete 删除移动命令对应的内容 dd delete 删除光标所在行 D delete 从光标位置删除至行尾 注: 如果使 ...
- 【linux】centos6/7 + nginx 利用certbot 申请https证书
没错我又踩坑了.昨晚上搞到十二点半才成功申请.鬼知道OJ服务器是个什么渣渣. 早上才算正式弄好,中间也学了不少东西,记录一下.这次是http转https,所以默认的还是只有80端口. 请务必确保自己的 ...
- input输入框数字转带千分位的字符串
数字转带千分位的字符串 思路 先获取要转换的数字,对其进行分割 小数部分具体需要保留多少位,具体处理 整数部分用正则做替换 将小数部分和整数部分合计 代码 注意: 本文是基于 jQuery 写的,稍稍 ...
- DDCTF 北京地铁
这周打了ddctf,被打成了dd 北京地铁题目给了一张北京地铁图,提示如下:Color Threshold 提示:AES ECB密钥为小写字母提示2:密钥不足位用\0补全提示3:不要光记得隐写不看图片 ...