Redux 是 JavaScript 状态容器,提供可预测化的状态管理。html
提供了和双向绑定思想不一样的单向数据流,应用状态能够预测,能够回溯,易于调试。使用redux之初的人可能会很不适应,改变一个状态,至少写三个方法,从这点上不如写其余框架代码易于理解,可是自从配合使用redux-logger一类的logger插件,就感受到了redux的优点。状态改变很清晰,很容易了解发生了什么。react
注意: 若是没有使用过redux,建议先去看看redux文档express
export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
能够看到咱们在react代码中使用到的api,通常主动调用的就是 combineReducers
,其余部分参照例子基本能够搬过来redux
打开combineReducers.js,先看export的方法,也就是combineReducers方法api
var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } }
首先看到这个函数接收的是一个对象,而这个这个对象的内部数据值必须是一个函数,否则会警告。循环了一遍这个对象,获得一个新值,对象值所有是函数的一个新reducers数组
var finalReducerKeys = Object.keys(finalReducers) if (process.env.NODE_ENV !== 'production') { var unexpectedKeyCache = {} } var sanityError try { assertReducerSanity(finalReducers) } catch (e) { sanityError = e }
这里好像还在判断这个最后reducers的合法性,那这里是在判断什么呢?咱们来看看 assertReducerSanity
方法app
function assertReducerSanity(reducers) { Object.keys(reducers).forEach(key => { var reducer = reducers[key] var initialState = reducer(undefined, { type: ActionTypes.INIT }) if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined.` ) } var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') if (typeof reducer(undefined, { type }) === 'undefined') { throw new Error( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined.` ) } }) }
这块其实就两个判断,reducer被执行了两次,一个是判断没有初始化state的,reducer的返回值,一个判断action没有type的时候的返回值。一个没有返回值都会有警告,因此咱们写reducer的时候都会指定一个默认返回值。框架
reducer会被执行屡次,这也是咱们为何要保证reducer的纯粹性,不能作任何其余的操做的缘由less
继续往下看 combineReducersdom
能够看到返回了一个函数 combination(state = {}, action)
。为何返回函数呢?
那咱们看 combination(state = {}, action)
像什么?不就是咱们常常写的reducer嘛!这个reducer最终会被store传入初始state而且看成纯函数调用,而reducer里面是能够嵌套combineReducers的结果的,因此咱们在使用状态的时候,常常会这样 state.user.login
这样子的相似状态调用
这块想明白仍是有点复杂,全部的reducer都是一个相同的函数combination,接收state参数,内部执行一样是combination,直到没有combineReducers为止,才开始执行咱们本身写的reducer函数,获得的值使用combineReducers参数的对象的key做为state的key,咱们本身写的reducers执行结果获得的值做为state的value。最终获得的就是一个巨大的Object,这就是咱们的store中的state。
通常这个方法咱们能够直接从demo中复制过来,不须要太过了解,可是既然要深刻了解redux,必然要掌握这个方法
跟以前同样,先找到 export createStore
方法,能够看到这个函数接受三个参数
export default function createStore(reducer, preloadedState, enhancer) {
第一个reducer: 上文讲到的combineReducer返回的reducer函数
第二个preloadedState:redux初始化state,能够不传
第三个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) } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') }
能够看到第一个判断的意思是当没有第二个参数是函数的时候,默认第二个参数就是中间件,而且默认state置为undefined
第二个判断的意思是当有中间件参数,可是中间参数类型不是function的时候,抛出一个非法错误,若是是函数,先执行中间件,退出。后续在讲中间件是怎么执行的
第三个判断reducer是不是函数,不然抛出错误退出
var currentReducer = reducer // 当前reducer var currentState = preloadedState // 当前state var currentListeners = [] // 当前监听器 var nextListeners = currentListeners // 下一个监听器 var isDispatching = false // 重复dispatch的状态标记
再看看createStore的返回值
return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
这不是store的方法嘛,挨个看看
function getState() { return currentState }
这个没什么好说的。
function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
发布订阅模式,熟悉事件系统的应该比较明白,注册一个方法而已,结果返回一个取消监听方法
function dispatch(action) { if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } return action }
老几样啊,先作一些判断,咱们写代码的时候好像没这么严谨哈。执行reducer,触发全部listeners。这个比较简单。
这样子,看起来createStore没什么复杂的,复杂的在哪呢?咱们掠过的中间件退出的环节。因此来烧脑吧,看看中间件
想一想咱们建立store的时候是怎么操做的
const finalCreateStore = compose( applyMiddleware(thunk, logger) )(createStore) const store = finalCreateStore(rootReducer, initialState)
这种堆在一块儿的代码不是太好看,分开,分开
const middlewares = applyMiddleware(thunk, logger) const composeResult = compose(middlewares) const finalCreateStore = composeResult(createStore) const store = finalCreateStore(rootReducer, initialState)
这就条理清晰多了,看代码必定要看懂流程,按照顺序看,否则一头雾水,先看第一步 applyMiddleware
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
能够看到这个方法返回一个函数,既然这个函数没有被执行到,咱们就先不看,如今咱们获得了一个 applyMiddleware
返回的函数了
接着看 compose
方法了
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }
代码更少,但是redux精髓全在这了。
compose 执行接收参数,若是参数个数是1,直接执行,上文的 applyMiddleware
的执行结果返回值是一个函数middlewares,做为参数的话,长度确实是1,因此直接返回了middlewares,也就是composeResult,因此这块是不须要compose的。而这个参数函数接收一个参数就是createStore,恰好接收createStore方法,因此咱们仍是进入到 applyMiddleware
的返回函数里面看看
显然 composeResult 接收到 createStore以后返回一个函数: finalCreateStore,从代码中能够看出也是能够接收中间件方法的,不过应该不会有人再在这里重复添加中间件了。
进入到 finalCreateStore
中看看
建立了store,前文已经讲过了
把全部的middlewares执行一遍,从这里能够看出middlewares是一个接收 { dispatch, getState }
参数的函数,不可能有其余状况
把middlewares执行的结果数组做为参数再一次传入了compose
再次进入到 compose
中看逻辑,若是只有一个中间件的话,一样是把中间件直接返回,若是超过一个执行下面的逻辑
const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
compose 一样只是返回了一个函数。这个函数接收的参数在 applyMiddleware
里面能看到接收到的是dispatch方法
这里巧妙的利用了js Array的reduce方法,reduce方法的原理就是回调函数的返回值做为后一个回调函数的第一个参数,第一个回调函数的第一个参数的值是 reduce方法的第二个参数值。
args就是dispatch方法,这里看的出中间件函数还得返回函数,这个函数得接收相似dispatch方法的函数
看看redux-chunk这个中间件的实现吧
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
看到 next 方法,使用过express的同窗应该会很熟悉,这个next和express的next很像,原理也相似。
每一个中间件的最后一层函数都是一个next,才能够在reduce里面做为参数传递,才能够实现中间件的传递
这也是redux名称的由来。
redux代码短小精悍,设计精巧,真好。