redux介绍
redux给咱们暴露了这几个方法webpack
{ createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
咱们来依次介绍下web
createStore
建立一个store的写法:redux
let store = createStore(reducer, preloadedState, enhancer);
createStore中的三个参数reducer, preloadedState, enhancer,后面两个是可选参数,
当咱们只传两个参数,而且第二个参数是函数时,例如:数组
let store = createStore(reducer, enhancer);
经过阅读源码这段数据结构
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined }
咱们知道,上面的createStore会被改写成这样:闭包
createStore(educer, undefined, enhancer)
再经过阅读源码这段:app
if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) }
会发现原来的调用方式再次发生了改变,变成了如下这种方式异步
enhancer(createStore)(reducer, undefined)
因此实例化一个createStore方法,如下两种是等价的:ide
第一种 const store = createStore(reducers, applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware)); 第二种 const store = applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware)(createStore)(reducers);
最后,返回的store对象提供了如下几个方法给咱们使用模块化
dispatch, subscribe, getState, replaceReducer,
getState用来获取currentState,也就是总state值
subscribe注册多个监听函数,这些函数在开发者调用dispatch时会依次执行
dispatch的入参是一个对象action,直接会执行reducer(action)方法,而且批量执行subscribe监听的内容,dispatch执行结束后会返回一个action
replaceReducer替换当前的reducer,这个方法在异步的单应用中可使用利用起来,例如,咱们不想一次性将全部的reducer交给createStore初始化,而是当异步获取某个页面时,再将这个页面的reducer加上以前的旧reducer,经过replaceReducer方法来替换成最新的。这样作的好处是,devtools里的redux工具不会展现那么多的信息,只会展现访问过页面的信息,更有利于咱们的开发,固然了因为reducer的减小,store的体积也会变小,页面执行速度更快。
combineReducers
源码
export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const 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] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }
一般咱们会这样使用
combineReducers({ a:reducer1, b:reducer2 })
reducer1是一个函数,而combineReducers返回的任然是一个函数,只不过将每一个reducer都遍历了一遍,最后返回的数据结构为
{ a:state1, b:state2 }
而若是咱们把a跟b替换成了一个惟一的路径path,这个path跟项目中每一个页面的文件夹对应起来,例如:
combineReducers({ 'component/page1/info/':reducer1, 'component/page2/user/':reducer2 })
而在取state数据的时候这样来取
state['component/page1/info/']是否是就能够经过文件夹路径实现模块化了,固然了咱们离模块化还差一小步,就是不用人工来写路径path,而是交给构建工具(webpack的loader)来遍历完成。
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(a,b,c)(d)后的代码就是a(b(c(d))),compose后返回的任然是一个函数,能够接受参数,compose在后面介绍的代码中有所涉及。
applyMiddleware
源码
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
建立一个store的方法:
let store = applyMiddleware(middleware1,middleware2,middleware3)(createStore)(reducers)
中间件middleware1的代码
function middleware1({ getState }) { return (next) => (action) => { console.log('will dispatch', action) let returnValue = next(action) console.log('state after dispatch', getState()) return returnValue } }
applyMiddleware传入的参数为多个中间件,中间件的做用是在执行reducers中的switch以前,先执行中间件中的代码。
对应源码中的参数来说的话,
实例参数middleware一、middleware2就至关于源码入参...middlewares,
实例参数createStore对应着源码入参createStore,
实例参数reducers对应着源码入参...args。
源码中的
const store = createStore(...args)
获取store对象供后面使用,middlewareAPI里的getState对应着store对象里的getState,getState()方法能够获取currentState,也就是redux对象树的总数据。
chain = middlewares.map(middleware => middleware(middlewareAPI))
middlewareAPI提供了getState这个方法,供中间件获取currenState,这时候chain获取的数组是一个返回值为函数的函数。
下面这行代码比较精彩
dispatch = compose(...chain)(store.dispatch)
首先将最原始的store.dispatch方法做为入参,
dispatch(action) 就至关于 compose(...chain)(store.dispatch)(action), 同时也至关于 middleware1(middleware2(middleware3((store.dispatch))))(action),
固然这里的middleware1是已经只剩下闭包内的两层函数不是原来的三层函数体。
最早执行的是middleware1, 返回了next(action), 也就至关于 middleware2(middleware3((store.dispatch)))(action), 执行完后返回next(action) 就至关于middleware3((store.dispatch))(action), 执行完后返回next(action) 就至关于store.dispatch(action)
整个过程咱们已经弄清楚了,applyMiddleware中间件的执行过程就是不断的next(action),
而只有最后的next才是执行dispatch,以前的next只表明的传递其余中间件,dispatch方法只在最后一个中间件里执行了一次。
我的以为这个地方结合了compose以后写的比较精彩,并且设计的很是巧妙。