Redux源码分析之applyMiddleware

小编 2026-06-05 阅读:1356 评论:0
Redux源码分析之基本概念Redux源码分析之createStoreRedux源码分析之bin...

Redux源码分析之基本概念

Redux源码分析之createStore

Redux源码分析之bindActionCreators

Redux源码分析之combineReducers

Redux源码分析之compose

Redux源码分析之applyMiddleware 

Redux 最为经典我觉得就是compose 和 applyMiddleware 了。

还是先借一张图,描述的非常准确,

  • 中间件是通过next来进入下一个中间件的,执行完毕后,会调用最原始的store.disptach,reducer执行完毕后,该次操作并没有完毕, 还会依次返回到中间件。
  • 任何一个中间件不next ,其后面的中间件都不会执行,(不等于return next(action),return next(action)一般情况都是返回原始的action, 只要你调用了next(action)就行),redux-thunk就是这么干的(检查到action是函数的时候,没有执行next())

Redux源码分析之applyMiddleware

 那么我们还是来看一个简单的例子,这里我把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.Reduxlet 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            }        })    }, 1000)}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: 1,        content: '学习redux'    }})

Redux源码分析之applyMiddleware

       从上面的例子,我们总结一下

  • 除了有效的更新数据,还通过中间件达到了额外的操作,比如输出日志,能够发异步的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。整体就是这个样子拉。 

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表