Redux是优秀的状态管理库,本节我们学习一下Redux源码,由于Redux源码是TypeScript写的,为了方便学习,本节去掉一些类型定义,转化为JavaScript来展示,另外对于错误信息我们这里就先不处理了。
使用
Redux官方示例:
1 | import { createStore } from 'redux' |
createStore
createStore简易实现:
1 | const randomString = () => |
上述是createStore的简单实现,调用createStore()函数以后返回一个store对象,该对象有4个方法,如下:
1:dispatch:分发action,通过currentReducer(currentState, action)来生成新的state,并触发事件。
2: subscribe: 监听事件,实际上就是把事件添加到事件数组中,并返回移除事件函数。
3: getState:获取当前的状态。
4: replaceReducer:替换reducer。
最新的源码与我们的实现理念大致相同,只是多了类型的校验,另外事件采用双map形式(防止dispatch中调用subscribe/unsubscribe)而不是我们简单的数组,最后在事件触发时会使用变量标记,防止在分发过程中出现不合理的操作。
中间件
中间件简绍
写一个redux中间件很简单,比如写一个打印日志的中间件。
1 | const logger = function(store) { |
中间件是一个嵌套三层的函数,每一层都有一个参数,参数分别是store、next、action。上面是redux-logger中间件的简单实现,常用的中间件还有redux-thunk,核心代码如下:
1 | const thunk = ({ dispatch, getState }) => next => action => { |
redux-thunk的逻辑也很简单,通过对store解构获取dispatch和getState函数,如果action是函数则调用action,否则调用next(action)进行下一个中间件。在action函数中可以通过dispatch来触发action,哪怕是在异步的回调中,所以redux-thunk通常用来处理异步操作。
中间件使用
1 | import { createStore, applyMiddleware } from 'redux' |
需要注意的是applyMiddleware是有顺序的,它会从做左到右依次执行,next后是从右到左执行。如果上述示例修改为applyMiddleware(logger, thunk)会发生什么事呢?那么在调用store.dispatch(() => {})的时候也会打印日志,里面的dispatch又会打印一次。
applyMiddleware实现
由上createStore函数可知,当传入中间件的时候会通过enhancer来生成store。enhancer实际上就是applyMiddleware(logger, thunk)的结果,它是一个两层函数,第一层接受的参数是createStore第二次接受的参数是reducer和preloadedState,代码大致如下:
1 | function compose(...funcs) { |
首先通过createStore(reducer, preloadedState)不传中间件来创建store,applyMiddleware内层函数的返回值只有dispatch是处理过的函数,其他的都是与store中的一致,也就是说中间件的作用实际上是强化dispatch函数。middlewareAPI实际上就是中间件的第一层函数的参数store,这里需要注意的是dispatch调用的时候,下面的代码已经走完了,所以里面的dispatch函数是加强后的dispatch而不是上面定义的抛出异常的函数。通过middlewares.map(middleware => middleware(middlewareAPI))去掉了中间件第一层函数。compose核心逻辑是funcs.reduce((a, b) => (...args) => a(b(...args)))对于函数数组返回嵌套执行的组合函数,compose(...chain)(store.dispatch)最后参数是store.dispatch,比如有两个中间件a和b,这里相当于变成a(b(store.dispatch)),相当于a这一层的next函数是b(store.dispatch)函数。