Redux 源码解读 & 中间件机制

前言

目前公司是基于React的技术栈,而我本身对于React的理解只限于应付项目业务开发而已,并且公司项目基本都是依赖umi框架机制,致使新人甚至对一些最基本的知识点都不了解就开始着手开发,好比react-router/react-reduxjavascript

大佬造的轮子用多了也是会有负面影响,为了避免让本身作一条只会用别人东西的咸鱼,决定去了解下redux的原理,由于redux源码相对比较简洁并且经过redux大体都能了解其余的状态管理工具的设计思路,一通则百通,无外乎这个道理,之后就算有几百个轮子均可以很快理解java

工做流程图

在了解源码以前先能够仔细看下Redux的总体流程图(感谢若川大佬的图,这里冒昧借用下),若是能简单看懂这张图,那么redux的源码学习将会很是快速react

调试

为了了解源码,就必需要学会调试代码,如今咱们去 redux@4.0.4 拷贝redux的代码到本地git

git clone https://github.com/reduxjs/redux.git
复制代码

能够很清晰的看到redux是使用rollup打包,为了能搞更好地调试,给它加上sourcemap配置github

//packages.json
"scripts": {
   "build": "rollup -c -m",
   ...
}
复制代码

执行yarn build命令,能够看到生成了es/dist/lib对应不一样的环境编程

接下来我是直接使用的官方examples文件夹里的async例子,直接把es文件复制进去,在examples/async/index.js 里更改引入json

// import { createStore, applyMiddleware } from 'redux'
import { createStore, applyMiddleware } from './es/redux.js'
复制代码

源码理解

不少文章都是从createStore开始介绍,我按照本身的思路,从combineReducers开始一步一步理解redux的机制redux

combineReducers

先看下一个redux官方给出的简单例子: /reducesc/index.jspromise

这里定义了postsBySubreddit,selectedSubreddit两个函数,即为两个reducer, 也是咱们项目中都会存在的,并且只会比它多。而在index.js中,须要把整合的rducer传入createStore中,下面咱们看看 combineReducers 对这两个reducer作了什么bash

能够很清晰得看到combineReducers获取两个reducer函数,通过简单地校验,生成了相似{ [function.name]: function }的键值对象, 并存放在闭包中(finalReducers), 最后返回了一个combination函数,用来传入createStore中做为形参,而combination具体作了什么下面会讲

createStore

reducer做为参数传入createStore中以后,createStore函数执行返回核心的store对象,顺着思路来看下createStore中作了什么

能够很清楚的看到 store就是返回 dispatchsubscribegetStatereplaceReducer等方法。工做中用到过 redux的应该很是熟悉。

如今咱们具体看下对传进来的reducer作了什么,其实上面都是一些方法的定义,重点要注意这句

// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT })
复制代码

这里单独执行一个distpatch方法,是为了初始化生成一个state tree, 也就是生成一个包含store里面全部state的树形结构对象,接下来主要看下dispatch作了什么, 而咱们以前传入的reducer也就在这里起了做用

dispatch

这里咱们须要关注的重点就是currentReducer(其实就是上文传过来的combination函数),这里调用了这个函数,并传入state/action

这里能够看出combination拿到最初的stateaction, 先作了简单的判断,以后也就是这里的一个问题:遍历执行全部的reducer来改变state

固然,最初的时候是造成了基本的树形结构state, 可是以后的每一步dispatch都要去遍历全部的reducer获得新的state进行比较再返回最终的state, 保存进createStore里的currentState中,能够经过store暴露的getState方法获取当前的state

subscribe

subscribe其实就是订阅发布模式的一种实现吧,我是这么理解的,每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。你能够在回调函数里调用 getState() 来拿到当前 state。它的代码也很简单

咱们在index.js中加入,而后开始调试,它会走到createStore中暴露的subscribe方法里

debugger;
store.subscribe(() => console.log(7))
复制代码

subscribe方法会把listener加入到nextListeners中,至关于发布订阅模式中的消息队列,以后在dispatch函数执行的时候,遍历nextListeners出发listener函数,具体代码在dispatch中查看,这里不作详述了

以上是对基本的redux机制作了简单的分析理解,咱们会发现这些功能还不足以彻底承载咱们的业务,因此redux自己引入中间件的概念对自己功能进行扩展。社区有不少中间件的开源库,包括redux-thunk,redux-promise,redux-saga等等等等,学是学不完的,但仍是那句话,一通百通的道理......

中间件机制

applyMiddleware

若是咱们要加入中间件,能够在createStore中加入参数applyMiddleware(...middlewares), 这里我以redux-thunk为例

import { createStore, applyMiddleware } from './es/redux.js'
import thunk from 'redux-thunk'
import reducer from './reducers'

const store = createStore(
  reducer,
  applyMiddleware(thunk)
)
复制代码

看一下applyMiddleware函数具体作了什么

这里applyMiddleware用了柯里化的理念,按上面的例子来讲第一个参数就是thunk,获取数据以后会在createStore里调用

接下来看下createStore中对于中间件的处理逻辑

这里的enhancer函数就是上方的applyMiddleware(thunk)返回函数,再次传入createStore以及初始化的reducerpreloadedState, 这表示又再次走到了applyMiddleware函数里面

这里跟上面同样生成了store对象,赋给middleware返回chain,这里最难理解的就是compose方法,也是中间件机制的关键所在

compose

这里其实也是函数式编程的一个组合的理念,把函数从右至左进行组合,能够理解为上个函数的返回就是下个函数的参数,若是仍是不明白这个reduce代码可能不理解,那咱们慢慢来理解它

首先先看个例子:

很明显结果是108, 执行顺序依次是multiply,add,minus,那接下来咱们把它写成通用的函数

redux中的compose跟这很相似,可是区别在于它返回的是双层函数,即next(...)函数,这里是中间件机制最难理解的地方,它就是经过这种方式来实现洋葱模型的

为了理解compose的组合机制,咱们先回来看下react-thunk的代码

接来下咱们再本身实现一个简单的middleware

在看下上面的调试的截图,多个中间件时,chain则等于[next => action => ..., next => action => ...], 再进入compose函数,获得的结构就是(...args) => a(b(...args)), 这时又传入store.dispatch(即next函数)

  1. 再观察下这两个中间件,store.dispatch传入以后,next被替代为store.dispatch 而返回了action => {}函数,这个函数又做为下一个中间件的next函数
  2. 当用户执行dispatch(action), 中间件接收到了action,则开始调用刚传入的 next函数指向外部next直至store.dispatch

如今咱们根据redux-thunk具体的例子来理解:

export const requestPosts = subreddit => ({
  type: REQUEST_POSTS,
  subreddit
})

const fetchPosts = subreddit => (dispatch, getState) => {
  dispatch(requestPosts(subreddit))
}

// dispatch action in redux-thunk
dispatch(fetchPosts(subreddit))
复制代码
  1. 根据react-thunk的代码,fetchPosts(subreddit)的返回值就是action, 而action是函数, 则return action(dispatch, getState, extraArgument);, 根据例子上的第二个返回函数(dispatch, getState) => {}由此而来

  2. 这里的action(dispatch, ...)中的dispatch则是compose组合返回的, requestPosts(subreddit)执行后再次进入react-thunk中间件,因为不是函数则执行next(action),这时候若是有其余的中间件,则根据compose继续向左执行

总结

经过以上对Redux源码的简单解析,大体理解了它自己的内部机制, 如今再到头部看那张流程图是否理解得更加透彻了呢

参考资料

Redux 官方文档

学习 redux 源码总体架构,深刻理解 redux 及其中间件原理

redux-thunk

相关文章
相关标签/搜索