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源码 真的很精简,目录结构如下: ...
随机推荐
- Cordova各个插件使用介绍系列(三)—$cordovaImagePicker从手机图库选择多张图片
详情链接地址:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/cordova-3-cordovaimagepicker/ 这是能从手 ...
- Matlab: 作图
控制图的大小 figure('position',[x0,y0,dx,dy]); figure(fig number); 显示图例 legend('leg1','leg2') depend on ho ...
- ICG_System之全自动代码生成器V2.0版本
大家好! 早在2014年本人就已经利用业余时间开发自己的ICG之代码生成器系统.依靠bootstrap的崛起本人也在不断完善此应用.目的是为了减少开发人员的工作量. 减少不必要的复制粘贴操作,该系统已 ...
- top命令总结
top命令主要用来观察和收集运行在系统上的进程的一些有用信息.ps只是一个快照,是ps命令执行的那一瞬间的系统中进程的快照.top则可以用于持续观察. 第一步,在命令行键入top,回车进入top管理界 ...
- indexOf和lastIndexOf的使用
indexOf()和 lastIndexOf()是返回位置index的两个方法:都是接收两个参数,其中,indexOf()方法从数组的开头(位 置 0)开始向后查找:lastIndexOf()方法则从 ...
- javascript检测当前浏览器是否为微信浏览器
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- 打开vmvare出现The VMware Authorization Service is not running。
win+r再输入cmd打开doc,输入services.msc打开服务,吧Vmware Authorization Service 更改为自动或者将其启动即可.
- 你猜这个题输出啥?-- java基础概念
最近在看java编程思想,大部分内容都觉得没啥意思,但是突然看到一个基本概念(似乎都忘了),于是写了测试题,我想这辈子也不会忘这个概念了. 题目如下: public class Suber exten ...
- require.js(浅聊)
一.require 了解requirejs之前首先明白什么是模块化: 1.什么是模块化? 模块化设计是指在对一定范围内的不同功能或相同功能不同性能.不同规格的产品进行功能分析的基础上,划分并设计出一系 ...
- English - Mosquitos
Smith's house is full of mosquitos. Every night they bite him. He can not sleep because the mosquito ...