Redux作为当前应用非常广泛的一个库,包含了很多值得学习的地方,对源码有所了解自然也能更好的进行使用。 首先让我们从最常用的createStore开始
createStore(reducers, [initState], [enhancer])
createStore作为最基础的方法其代码并不复杂,它创建了一个闭包用于存放reducer, state, listeners等内容,并暴露这些变量的操作方法,从而完成store的创建。 每个store包含了4个方法,dispatch, subscribe, getState, replaceReducer, 其实还有一个方法 observable ,这个方法主要用于observable/reactive的库,用于监听state的变化,详见。
redux中最强大的莫过于enhancer了,它允许对创建或修改store对象,以及改变其默认的行为。applymiddlewares , redux-devtools 均是通过enhancer对redux进行了加强,因此redux本身,它的逻辑很轻,解耦解的很彻底。
创建后的store对象含有4个方法:
- dispatch: 发送一个action至reducer中用于更新当前的state, 并在reducer执行完成后调用当前store中存储的监听器。
- getState: 获取当前最新的state状态。
- subscribe: 添加一个监听器,并返回一个用于解除监听的回调函数。
- replaceReducer: 替换当前的reducer。
reducers 应该是我们平时接触得最多的一个内容了,它本身其实只是一个方法,当dispatch被调用时,将action及当前的state对象传入到reducer中,从而获取到新的state对象值,从而使整个redux形成一个数据流。createStore本身并不提供非方法的reducer支持,因此需要借助 combineReducers 的力量,将state进行拆分从而进行对象的分割。不过对于store来说,reducer始终只是一个方法。
subscribe 用于添加一个监听state变化的listener,当有action被dispatch以后,listener便会被触发。
官方文档中提到,一个 subscribe 不应该注意到所有 state 的变化,在编译器被调用之前,由于 dispatch 的嵌套,state 可能已经发生了多次的改变,因此并不是每次dispatch方法都会引起该方法被执行。
其次监听器在运行时还有一点需要注意,监听器列表会在执行前被浅拷贝一份,因此监听器运行期间的subscribe/unsubscribe方法的执行并不会影响到本次的运行。
接下来是applyMiddlware,相信下面这段代码大家应该不陌生
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducer from './reducer';
createStore(reducer, applyMiddleware(thunk, logger));
redux-thunk redux-logger 这些库都是middleware的实现,来看看applyMiddleware方法吧。
applyMiddleware
我们先来看下最简单的middleware代码模板
// api {object}
// api.getState {function} 获取当前的state状态
// api.dispatch {function} 发送一个action
//
// next:下一个middleware的实际逻辑,需要将action传入
// action: redux的action对象
const middleware = api => next => (action) => {
// do something
}
middleware的模版是用来创建一个自定义middleware。middlware主要用来对dispatch方法进行扩展及自定义。
applyMiddleware主要的有效代码集中在下面两行
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
从上面的代码中可以看到所有的middleware通过第一次方法的执行会被传入一个API对象,对象内包含了getState与dispatch函数。 由于每个middleware中传入了相同的API对象,因此对象中的方法其实是可以被 复写 的,可以实现自己的dipsatch方法以实现相关逻辑。
由于第一次执行时,通过api对象传入的dispatch会被复写并嵌套,因此可以看作是store的dispatch,是整个chain的起点。
后续具体的实现中,需要不断的将上一次的运行结果传递下去,因此许通过compose方法将所有的方法连接起来产生嵌套关系,例如 m1(m2(m3(m4(...args))))
,这时,整个chain的顺序是逆向的(从后到前),此时通过执行chain可以很好的将redux的dispatch方法传递给最后一个middleware实现,并构建起正确的顺序。
compose
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
compose本质上就是一个方法数组的reduce,依据顺序将后一个方法执行的结果传递给前一个,第一个方法最后执行。
compose由于只是一种工具方法,因此它相较于 applyMiddleware 并不仅限于某种特定的场景。
compose示例,createStore的enhancer使用
// enhancer
const enhancer = (createStore) => (reducer, initState) => {
// #1: createStore
const store = createStore(reducer, initState);
const { dispatch } = store;
const dispatchProxy = (action, ...args) => {
console.log(`before doing action ${action.type}`);
dispatch(action, ...args);
console.log(`after doing action ${action.type}`);
};
return {
...store,
dispatch: dispatchProxy,
}
};
const store = createStore(reducer, compose(enhancer, applyMiddleware()));
示例代码中的enhancer便是一个使用compose场景,代码中对store的dispatch方法做了扩展。每次调用dispatch时,会在调用前及调用后输出一段日志,记录当前执行的action类型。
对于示例代码中 #1 处的createAction其实并不是redux本身的createAction,它是由 applyMiddleware 生成的。 所以 enhancer 中的dispatch等方法,其实已经经过了 applyMiddlware 的扩展。
bindActionCreators
这个方法用的比较少,该方法接收两个参数 actionCreators , dispatch
其中 actionCreator 是一个 Map<string, function>
对象,dispatch 方法对应store的dispatch方法
当前方法会遍历 actionCreator 并返回一个新的map对象,新map对象中的所有方法都会替换为由下面这段方法创建的一个proxy
proxy用于将原方法的执行结果直接传递给 dispatch 方法。
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}