redux middleware 源码分析
middleware 的由来
在业务中需要打印每一个 action 信息来调试,又或者希望 dispatch 或 reducer 拥有异步请求的功能。面对这些场景时,一个个修改 dispatch 或 reducer 代码有些乏力,我们需要一个可组合的、自由增减的插件机制,Redux 借鉴了 Koa 中 middleware 的思想,利用它我们可以在前端应用中便捷地实现如日志打印、异步请求等功能。
比如在项目中,进行了如下调用后,redux 就集成了 thunk 函数调用以及打印日志的功能。
import thunk from 'redux-thunk'
import logger from '../middleware/logger'
const enhancer = applyMiddleware(thunk, logger), // 以 redux-thunk、logger 中间件为例介绍中间件的使用
const store = createStore(rootReducer, enhancer)
下面追本溯源,来分析下源码。
applyMiddleware 调用入口
export default function createStore(reducer, preloadedState, enhancer) {
// 通过下面代码可以发现,如果 createStore 传入 2 个参数,第二个参数相当于就是 enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(reducer, preloadedState)
}
...
}
由上述 createStore 源码发现,applyMiddleware 会进行 applyMiddleware(thunk, logger)(createStore)(reducer, preloadedState) 的调用。
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState, // 调用 redux 原生方法,获取状态
dispatch: (...args) => dispatch(...args) // 调用 redux 原生 dispatch 方法
}
// 串行 middleware
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // 返回加工过的 dispatch
}
}
}
可以发现 applyMiddleware 的作用其实就是返回加工过的 dispatch,下面会着重分析 middlewares 是如何串行起来的以及 dispatch 是如何被加工的。
串行 middleware
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
观察上述代码后发现每个 middleware 都会传入参数 middlewareAPI,来看下中间件 logger 的源码 以及 redux-thunk 的源码, 发现中间件接受的第一个参数正是 ({ dispatch, getState })
// logger 源码
export default ({ dispatch, getState }) => next => action => {
console.log(action)
return next(action) // 经 compose 源码分析,此处 next 为 Store.dispatch
}
// redux-thunk 源码
export default ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch)
}
return next(action) // 此处 next 为 logger 中间件返回的 (action) => {} 函数
}
dispatch 是如何被加工的
接着上个小节,在 dispatch = compose(...chain)(store.dispatch) 中发现了 compose 函数,来看下 compose 的源码
export default function compose(...funcs) {
// ...
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose 源码中的 funcs.reduce((a, b) => (...args) => a(b(...args))) 算是比较重要的一句,它的作用是返回组合参数后的函数,比如 compose(f, g, h) 等价于 (...args) => f(g(h(...args))),效果图如下所示,调用 this.props.dispatch() 后,会调用相应的中间件,最终会调用 redux 原生的 store.dispatch(),并且可以看到中间件调用的形式类似数据结构中的栈(先进后出)。
拿上个小节提到的 logger、redux-thunk 中间件为例,其 middleware 的内部串行调用方式如下,从而完成了 dispatch 功能的增强(支持如 this.props.dispatch(func) 的调用以及日志功能)。具体可以看 项目中的运用
action => {
if (typeof action === 'function') {
return action(dispatch)
}
return (action => {
console.log(action)
return store.dispatch(action)
})(action)
}
参考文献
深入React技术栈
redux middleware 源码分析的更多相关文章
- Redux源码分析之createStore
接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...
- Redux源码分析之applyMiddleware
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之compose
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- 正式学习React(四) ----Redux源码分析
今天看了下Redux的源码,竟然出奇的简单,好吧.简单翻译做下笔记: 喜欢的同学自己可以去github上看:点这里 createStore.js import isPlainObject from ' ...
- Redux源码分析之基本概念
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之bindActionCreators
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之combineReducers
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Koa源码分析(三) -- middleware机制的实现
Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...
- 大熊君大话NodeJS之 ------ Connect中间件第二季(源码分析)
一,开篇分析 大家好,大熊君又回来了,今天这篇文章主要是对"Connect"中间件以及相关辅助中间件,做一个源码分析系列,我想上一篇文章大家也看了, 介绍了使用方式及用途,而这篇也 ...
随机推荐
- Java接口和抽象类的理解
接口和抽象类的相同之处就是 都会有抽象方法 抽象方法就是一个没有方法体 等待继承的子类完成的方法 然而接口比较严格 它的方法必须是抽象方法且是公开的 抽象类 可以有自己的属性 和 实体方法 首相用面向 ...
- JSP之JSTL_functions
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java ...
- python_15_内置函数
有哪些内置函数? -- all() - 可迭代对象,全为真返回真 -- any() - ...
- 微信H5支付网络环境未能通过安全验证,请稍后再试(获取终端ip )
在写微信H5支付的时候需要获取终端IP使用官方的方法是不对的报错如下: 故重写一个:如下 function get_client_ip(){ if(getenv('HTTP_CLIENT_IP') & ...
- asp.net 文件上传 Uploadify HTML5 带进度条
参考的https://www.cnblogs.com/lvdabao/p/3452858.html这位,在此基础上略有修改: 1.根据Layer,将上传附件做成弹窗显示,引入frame弹窗,在项目当中 ...
- WebService 的工作原理
Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的 ...
- 为什么webstrom无法格式化代码?
用过webstrom的童鞋都知道格式化代码需要按快捷键:ctrl+Alt+L:可是我最近发现这个快捷键不管用,今天终于知道了原因, 是后台网易云音乐没有关,和网易云音乐的快捷键冲突了,就是这么神奇没办 ...
- BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]
传送门 题意: 路径有$-1,1$两种权值,求有多少路径满足权值和为$0$且有一个点将路径分成权值和为$0$的两段 第四节课本来想去上化学,然后快上课了这道题还没调出来.....可恶我想上化学 昨天两 ...
- POJ 1222 EXTENDED LIGHTS OUT [高斯消元XOR]
题意: $5*6$网格里有一些灯告诉你一开始开关状态,按一盏灯会改变它及其上下左右的状态,问最后全熄灭需要按那些灯,保证有解 经典问题 一盏灯最多会被按一次,并且有很明显的异或性质 一个灯作为一个方程 ...
- ES6字符串
1.unicode表示法: alert("\u0061"); alert("\uD842\uDFB7"); alert("\u20BB7") ...