目前公司是基于React
的技术栈,而我本身对于React
的理解只限于应付项目业务开发而已,并且公司项目基本都是依赖umi
框架机制,致使新人甚至对一些最基本的知识点都不了解就开始着手开发,好比react-router
/react-redux
javascript
大佬造的轮子用多了也是会有负面影响,为了避免让本身作一条只会用别人东西的咸鱼,决定去了解下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
先看下一个redux
官方给出的简单例子: /reducesc/index.js
promise
这里定义了postsBySubreddit
,selectedSubreddit
两个函数,即为两个reducer
, 也是咱们项目中都会存在的,并且只会比它多。而在index.js
中,须要把整合的rducer
传入createStore
中,下面咱们看看 combineReducers
对这两个reducer
作了什么bash
能够很清晰得看到combineReducers
获取两个reducer
函数,通过简单地校验,生成了相似{ [function.name]: function }
的键值对象, 并存放在闭包中(finalReducers
), 最后返回了一个combination
函数,用来传入createStore
中做为形参,而combination
具体作了什么下面会讲
当reducer
做为参数传入createStore
中以后,createStore
函数执行返回核心的store
对象,顺着思路来看下createStore
中作了什么
store
就是返回
dispatch
、
subscribe
、
getState
、
replaceReducer
等方法。工做中用到过
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
也就在这里起了做用
这里咱们须要关注的重点就是currentReducer
(其实就是上文传过来的combination
函数),这里调用了这个函数,并传入state
/action
这里能够看出combination
拿到最初的state
和action
, 先作了简单的判断,以后也就是这里的一个问题:遍历执行全部的reducer
来改变state
固然,最初的时候是造成了基本的树形结构state
, 可是以后的每一步dispatch
都要去遍历全部的reducer
获得新的state
进行比较再返回最终的state
, 保存进createStore
里的currentState
中,能够经过store
暴露的getState
方法获取当前的state
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
等等等等,学是学不完的,但仍是那句话,一通百通的道理......
若是咱们要加入中间件,能够在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
以及初始化的reducer
和preloadedState
, 这表示又再次走到了applyMiddleware
函数里面
这里跟上面同样生成了store
对象,赋给middleware
返回chain
,这里最难理解的就是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
函数)
store.dispatch
传入以后,next
被替代为store.dispatch
而返回了action => {}
函数,这个函数又做为下一个中间件的next
函数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))
复制代码
根据react-thunk
的代码,fetchPosts(subreddit)
的返回值就是action
, 而action
是函数, 则return action(dispatch, getState, extraArgument);
, 根据例子上的第二个返回函数(dispatch, getState) => {}
由此而来
这里的action(dispatch, ...)
中的dispatch
则是compose
组合返回的, requestPosts(subreddit)
执行后再次进入react-thunk
中间件,因为不是函数则执行next(action)
,这时候若是有其余的中间件,则根据compose
继续向左执行
经过以上对Redux
源码的简单解析,大体理解了它自己的内部机制, 如今再到头部看那张流程图是否理解得更加透彻了呢