Redux源码分析之applyMiddleware

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-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
            }
        })
    }, 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'
    }
})

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

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

优秀的个人博客,低调大师

微信关注我们

原文链接:https://yq.aliyun.com/articles/612975

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Apache Tomcat7、8、9(Java Web服务器)

Apache Tomcat7、8、9(Java Web服务器)

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。