Redux源码分析之applyMiddleware
Redux 最为经典我觉得就是compose 和 applyMiddleware 了。
还是先借一张图,描述的非常准确,
- 中间件是通过next来进入下一个中间件的,执行完毕后,会调用最原始的store.disptach,reducer执行完毕后,该次操作并没有完毕, 还会依次返回到中间件。
- 任何一个中间件不next ,其后面的中间件都不会执行,(不等于return next(action),return next(action)一般情况都是返回原始的action, 只要你调用了next(action)就行),redux-thunk就是这么干的(检查到action是函数的时候,没有执行next())

那么我们还是来看一个简单的例子,这里我把redux-thunk的核心代码直接copy过来,放在一起了。
// thunk 中间件
let thunk = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
// logger中间件
let logger = ({ dispatch, getState }) => next => action => {
console.log('next:之前state', getState())
let result = next(action)
console.log('next:之前state', getState())
return result
} let { createStore, applyMiddleware } = self.Redux
let todoList = [] // 默认值
let todoReducer = function (state = todoList, action) {
switch (action.type) {
case 'add':
return [...state, action.todo]
case 'delete':
return state.filter(todo => todo.id !== action.id)
default:
return state
}
}
let addAsync = content => (dispatch) => {
setTimeout(function () {
dispatch({
type: 'add',
todo: {
id: new Date().getTime(),
content
}
})
}, )
} let store = createStore(todoReducer, applyMiddleware(logger)),
subscribe1Fn = function () {
console.log(store.getState())
} // 订阅
let sub = store.subscribe(subscribe1Fn) store.dispatch(addAsync('异步添加的todo哦'))
store.dispatch({
type: 'add',
todo: {
id: ,
content: '学习redux'
}
})

从上面的例子,我们总结一下
- 除了有效的更新数据,还通过中间件达到了额外的操作,比如输出日志,能够发异步的action,这就是中间件的神奇之处
- 这里有异步action,这就是中间件(redux-thunk)的力量
- 中间件的格式一般都是 ({ dispatch, getState }) => next => action => {......}, 为什是这样先不做分析
- 执行顺序 中间件 =>订阅=>中间件
回归源码,applyMiddleware.js,先删除一些代码,很容易理解
- 创建一个store
- 返回一个改写过dispatch方法的store
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch // 存旧的dispatch
.........
/生成新的dispatch
dispatch = compose(...chain)(store.dispatch)
// 返回改写过disptach的store
return {
...store,
dispatch
}
}
}
applyMiddleware 并不神奇,其他地方都很理解,内外层的参数传递都是围绕着 createStore来的。
那我们也不难理解应该怎么调用这个方法,应该像如下这样
- 传入 thunk, logger等等各种中间件,
- 接着传入我们创建store的方法createStore
- 最后传入reducer,preloadState,enhancer
let store = applyMiddleware(thunk, logger)(createStore)(todoReducer),
可是回头看看我们的代码
let store = createStore(todoReducer, applyMiddleware(thunk,logger)),
那我们再回来看看createStore方法
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
.......
}
- 如果 preloadState是函数,并且enhancer为空, enhancer =preloadState
- 接着,如果有enhancer ,那么 return enhancer(createStore)(reducer, preloadedState)
结合我们的调用分析一下
createStore(todoReducer, applyMiddleware(thunk,logger))
- preloadState是函数,并且enhancer为空, enhancer = applyMiddleware(thunk,logger)
- return enhancer(createStore)(reducer, preloadedState) = return applyMiddleware(thunk,logger)(createStore)(reducer, preloadedState)
所以嘛,createStore(todoReducer, applyMiddleware(thunk,logger)) 只是一种变体,更加方便调用而已,这里也正式了 applyMiddleware(thunk,logger) 也是store的一个增强函数
我们最后在看看看我们忽略的代码
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => { // createStore就是redux公布的方法creatStore,
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch // 存旧的dispatch
let chain = []
/*
中间件标准格式
let logger1 = ({ dispatch, getState }) => next => action => {
...
let result = next(action)
...
return result
}
*/
//构建参数 ({dispatch, getState})
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
/*
middleware(middlewareAPI)之后是这样的格式
let m = next => action => {
...
let result = next(action)
...
return result
}
*/
chain = middlewares.map(middleware => middleware(middlewareAPI))
//生成新的dispatch
dispatch = compose(...chain)(store.dispatch)
// 返回改写过disptach的store
return {
...store,
dispatch
}
}
}
我滴个神,就三句,第一句,不多说了,给所有的中间件,初始化参数,
这也就是为什么,所有中间件的格式都是第一层参数都是 {disptach,getState}这个样子
({dispatch, getState} ) => next => action => {......}
第二句就是把每个middleware传入参数,初始化一下,这里的最大作用就是利用闭包,让每个middleware拥有同一份disptach和getState的引用。
执行后,每个middleware返回的函数式这个样子的, chain保存着这种函数的集合
next => action => {
...
let result = next(action)
...
return result
}
剩下最核心的一句
dispatch = compose(...chain)(store.dispatch)
compose之前已经详细的解读过了,就是生成链式的调用,是把 f,g,h 变成 (...args) => f(g(h(...args))),现在f,g,h的格式如下,
next => action => {
...
let result = next(action)
...
return result
}
可想而知,这样的函数是整个作为前面一个函数的next参数存在的,所以你每次next(action)实际上就是进入下一个中间件的执行体,
接着把 store.dispatch 作为next参数传入,作为了最内层,也是最后一个中间件的next,返回的函数格式就是下面这个样子了,我们替换一下参数
action => {
...
let result = next(action)
...
return result
}
等于
action => {
...
let result = store.dispatch(action) // 真正的dispatch action
...
return result
}
这个最后庞大的函数被赋值给了store,替换掉了原来的dispatch。整体就是这个样子拉。
Redux源码分析之applyMiddleware的更多相关文章
- Redux源码分析之createStore
接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...
- 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源码分 ...
- Redux源码分析之compose
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- 正式学习React(四) ----Redux源码分析
今天看了下Redux的源码,竟然出奇的简单,好吧.简单翻译做下笔记: 喜欢的同学自己可以去github上看:点这里 createStore.js import isPlainObject from ' ...
- 史上最全的 Redux 源码分析
前言 用 React + Redux 已经一段时间了,记得刚开始用Redux 的时候感觉非常绕,总搞不起里面的关系,如果大家用一段时间Redux又看了它的源码话,对你的理解会有很大的帮助.看完后,在回 ...
- redux源码学习笔记 - applyMiddleware
在创建store时,createStore(reducer, preloadedState, enhancer),除了reducer函数,初始状态,还可以传入enhancer.这个enhancer在c ...
- redux源码图解:createStore 和 applyMiddleware
在研究 redux-saga时,发现自己对 redux middleware 不是太了解,因此,便决定先深入解读一下 redux 源码.跟大多数人一样,发现 redux源码 真的很精简,目录结构如下: ...
随机推荐
- Webpack 3 中的新特性
本文简短地分享下最新发布的 Webpack 3 中的新特性,供大家参考. 1. Webpack 3 的新特性 6 月 20 日,Webpack 发布了最新的 3.0 版本,并在 Medium 发布了公 ...
- ionic 项目中添加modal的步骤流程
1.首先在templates文件夹下面定义一个新页面,xxx.html,template文件夹在空项目里面是没有的,需要手动添加一个,放在WWW文件夹下面. <ion-modal-view> ...
- 使用ConfuserEx加密混淆程序以及如何脱壳反编译
一,准备如下工具: ConfuserEx.UnConfuserEx.Fixer.ConfuserExStringDecryptor.ConfuserExSwitchKiller.de4dot.ILSp ...
- python模块之argparse--参数解析
一.简介: argparse是python用于解析命令行参数和选项的标准模块,用于代替已经过时的optparse模块.argparse模块的作用是用于 解析命令行参数,例如python parseTe ...
- js传宗接代---继承
前几天重温了一下js的继承,今天分享给大家: 一,类式继承. 所谓的类式继承就是:第二个类的原型prototype被赋予了第一个类的实例,如subcals.prototype=new supercls ...
- Ubuntu命令模式基础
Ubuntu是一个自由.开源.基于Debian的Linux发行版.在ubuntu的基础上,又衍生其它各具特色的Linux发行版.首先是一个操作系统,操作系统用于管理电脑硬件.要发挥出电脑的作用,还得依 ...
- 使用c#解析json库
写了个c#版的json解析库,提供了json到hashtable以及hashtable到json字符串的转换 受惠于c#的语法特性,hashtable到json的解析变得非常简单 先判断传入的obje ...
- grep[行号&正则匹配字符有颜色]
事情是这样的,昨天在深入学习grep命令时,看到别人博客用grep正则匹配,不仅行数有颜色,而且匹配到的字符也有颜色.我在CRT也试了下,毛颜色都没有.顿时感觉 so low. 解决 编辑vim~/. ...
- 关于Latex中插入Visio图片文字不显示的问题
经过探索,将Visio保存为pdf格式是最完美的解决方式,因为pdf文件保存了所有格式和字体信息. Visio输出pdf时要使其符合PDF/A标准.如果包含Visio的多余信息,就会在一些低版本Lat ...
- angularJS 源码阅读之一:toDebugString
简介: 这个函数返回调试字符串: number,boolean,string,null,undefined,都会转为字符串. function 中括号前面有空格的,会去除函数体,没空格的,会输出函数的 ...