一、本文不涉及redux的使用方法,所以可能更适合使用过 redux 的同窗阅读
二、当前redux版本为4.0.1
三、 更多系列文章请看
Redux做为大型React应用状态管理最经常使用的工具。虽然在平时的工做中不少次的用到了它,可是一直没有对其原理进行研究。最近看了一下源码,下面是我本身的一些简单认识,若有疑问欢迎交流。git
结合使用场景咱们首先来看一下createStore
方法。github
// 这是咱们日常使用时建立store const store = createStore(reducers, state, enhance);
如下源码为去除异常校验后的源码,redux
export default function createStore(reducer, preloadedState, enhancer) { // 若是有传入合法的enhance,则经过enhancer再调用一次createStore if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) // 这里涉及到中间件,后面介绍applyMiddleware时在具体介绍 } let currentReducer = reducer //把 reducer 赋值给 currentReducer let currentState = preloadedState //把 preloadedState 赋值给 currentState let currentListeners = [] //初始化监听函数列表 let nextListeners = currentListeners //监听列表的一个引用 let isDispatching = false //是否正在dispatch function ensureCanMutateNextListeners() {} function getState() {} function subscribe(listener) {} function dispatch(action) {} function replaceReducer(nextReducer) {} // 在 creatorStore 内部没有看到此方法的调用,就不讲了 function observable() {} //初始化 store 里的 state tree dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
咱们能够看到creatorStore方法除了返回咱们经常使用的方法外,还作了一次初始化过程dispatch({ type: ActionTypes.INIT })
;那么dispatch干了什么事情呢?闭包
/** * dispath action。这是触发 state 变化的唯一途径。 * @param {Object} 一个普通(plain)的对象,对象当中必须有 type 属性 * @returns {Object} 返回 dispatch 的 action */ function dispatch(action) { // 判断 dispahch 正在运行,Reducer在处理的时候又要执行 dispatch if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { //标记 dispatch 正在运行 isDispatching = true //执行当前 Reducer 函数返回新的 state currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) //遍历全部的监听函数 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() // 执行每个监听函数 } return action }
这里dispatch
主要作了二件事情app
reducer
更新state
那么reducer是怎么改变state的呢?这就涉及到下面的combineReducers
了框架
回到上面建立store的参数reducers,异步
// 两个reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 这是咱们日常使用时建立store const store = createStore(reducers, state, enhance);
下面咱们来看combineReducers
作了什么函数
export default function combineReducers(reducers) { // 第一次筛选,参数reducers为Object // 筛选掉reducers中不是function的键值对 const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) // 二次筛选,判断reducer中传入的值是否合法(!== undefined) // 获取筛选完以后的全部key let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { let hasChanged = false const nextState = {} // 遍历全部的key和reducer,分别将reducer对应的key所表明的state,代入到reducer中进行函数调用 for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] // 这里就是reducer function的名称和要和state同名的缘由,传说中的黑魔法 const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 将reducer返回的值填入nextState nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 发生改变了返回新的nextState,不然返回原先的state return hasChanged ? nextState : state } }
reducer(previousStateForKey, action)
执行的就是咱们上面定义的todos
和filterStatus
方法。经过这二个reducer
改变state
值。reducer
里的state
是怎么作到取当前reducer
对应的数据。看到const previousStateForKey = state[key]
这里我就明白了。combineReducers
的嵌套,最开始也我不明白,看了源码才知道combineReducers()=> combination(state = {}, action)
,这里combineReducers
返回的combination
也是接受(state = {}, action)
也就是一个reducer
因此能够正常嵌套。看到这初始化流程已经走完了。这个过程咱们认识了dispatch
和combineReducers
;接下来咱们来看一下咱们本身要怎么更新数据。工具
用户更新数据时,是经过createStore
后暴露出来的dispatch
方法来触发的。dispatch
方法,是 store
对象提供的更改 currentState
这个闭包变量的惟一建议途径(注意这里是惟一建议途径,不是惟一途径,由于经过getState获取到的是state的引用,因此是能够直接修改的。可是这样就不能更新视图了)。
正常状况下咱们只须要像下面这样源码分析
//action creator var addTodo = function(text){ return { type: 'add_todo', text: text }; }; function TodoReducer(state = [], action){ switch (action.type) { case 'add_todo': return state.concat(action.text); default: return state; } }; // 经过 store.dispatch(action) 来达到修改 state 的目的 // 注意: 在redux里,惟一可以修改state的方法,就是经过 store.dispatch(action) store.dispatch({type: 'add_todo', text: '读书'});// 或者下面这样 // store.dispatch(addTodo('读书'));
也就是说dispatch
接受一个包含type
的对象。框架为咱们提供了一个建立Action
的方法bindActionCreators
下面来看下源码
// 核心代码,并经过apply将this绑定起来 function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } } export default function bindActionCreators(actionCreators, dispatch) { // 若是actionCreators是一个函数,则说明只有一个actionCreator,就直接调用bindActionCreator if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } // 遍历对象,而后对每一个遍历项的 actionCreator 生成函数,将函数按照原来的 key 值放到一个对象中,最后返回这个对象 const keys = Object.keys(actionCreators) const boundActionCreators = {} for (let i = 0; i < keys.length; i++) { const key = keys[i] const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
bindActionCreators
的做用就是使用dispatch
把 action creator
包裹起来,这样咱们就能够直接调用他们了。这个在日常开发中不经常使用。
最后咱们回头来看一下以前调到的中间件,
import thunkMiddleware from 'redux-thunk'; // 两个reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 这是咱们日常使用时建立store const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));
为了下文好理解这个放一下redux-thunk
的源码
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;
能够看出thunk
返回了一个接受({ dispatch, getState })
为参数的函数
下面咱们来看一下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.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 每一个 middleware 都以 middlewareAPI 做为参数进行注入,返回一个新的链。此时的返回值至关于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next做为其参数 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 并将链代入进 compose 组成一个函数的调用链 // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。 // 在目前只有 thunkMiddleware 做为 middlewares 参数的状况下,将返回 (next) => (action) => {} // 以后以 store.dispatch 做为参数进行注入注意这里这里的store.dispatch是没有被修改的dispatch他被传给了next; dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } // 定义一个代码组合的方法 // 传入一些function做为参数,返回其链式调用的形态。例如, // compose(f, g, h) 最终返回 (...args) => f(g(h(...args))) export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } }
我第一眼看去一脸闷逼,咋这么复杂。可是静下心来看也就是一个三级柯里化的函数,咱们从头来分析一下这个过程
// createStore.js if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) }
也就是说,会变成这样
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
applyMiddleware(thunkMiddleware)
applyMiddleware
接收thunkMiddleware
做为参数,返回形如(createStore) => (...args) => {}
的函数。
applyMiddleware(thunkMiddleware)(createStore)
以 createStore
做为参数,调用上一步返回的函数(...args) => {}
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
以(reducer, preloadedState)
为参数进行调用。 在这个函数内部,thunkMiddleware
被调用,其做用是监测type
是function
的action
所以,若是dispatch的action返回的是一个function,则证实是中间件,则将(dispatch, getState)做为参数代入其中,进行action 内部下一步的操做。不然的话,认为只是一个普通的action,将经过next(也就是dispatch)进一步分发
也就是说,applyMiddleware(thunkMiddleware)
做为enhance
,最终起了这样的做用:
对dispatch
调用的action
进行检查,若是action
在第一次调用以后返回的是function
,则将(dispatch, getState)
做为参数注入到action
返回的方法中,不然就正常对action进行分发,这样一来咱们的中间件就完成喽~
所以,当action内部须要获取state,或者须要进行异步操做,在操做完成以后进行事件调用分发的话,咱们就可让action 返回一个以(dispatch, getState)为参数的function而不是一般的Object,enhance就会对其进行检测以便正确的处理
到此redux
源码的主要部分讲完了,若有感兴趣的同窗能够去看一下我没讲到的一些东西转送门redux;