Open duhongjun opened 6 years ago
然后我们看一下源码的目录结构:
看起来确实也没有多少, 接下来我们一个一个看
大致的结构如下(省略了一些很好理解的参数判断/抛错逻辑)
function createStore(reducer, preloadedState, enhancer) { if (typeof enhancer !== 'undefined') { // 会和applyMiddleware一起说 return enhancer(createStore)(reducer, preloadedState) } let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false // 获取当前state function getState() { // ... } // 增加监听函数, 每当调用dispatch时会触发 function subscribe(listener) { // ... } // 触发一个action, 执行对应的reducer更新state,并且执行所有通过`subscribe`注册的监听函数 function dispatch(action) { // ... } // 替换当前的reducer function replaceReducer(nextReducer) { // ... } // 对于这个函数,是不直接暴露给开发者的,它提供了给其他观察者模式/响应式库的交互操作, // 具体可看 https://github.com/tc39/proposal-observable function observable() { // ... } // 当一个store被创建, 一个"INIT" action会被触发, // 所以每个reducer 返回他们的初始值从而填充state树 dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
function getState() { // 没啥好说的.. return currentState }
执行 reducer, 更新 state, 然后遍历执行所有通过subscribe注册过的监听函数
subscribe
function dispatch(action) { try { isDispatching = true 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 }
内部有一个isSubscribed变量标识是否已经订阅,将监听函数放入nextListeners, 返回一个unsubscribe函数用于解绑, 需要注意的是无论绑定还是解绑都是在下次dispatch触发时才会生效
isSubscribed
nextListeners
unsubscribe
dispatch
// 先判断是否是同一个引用, 如果是会先进行拷贝 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } function subscribe(listener) { let isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
替换reducer, 并触发一个REPLACE action, 更新 store 的初始值
reducer
REPLACE
function replaceReducer(nextReducer) { currentReducer = nextReducer dispatch({ type: ActionTypes.REPLACE }) }
我们通常是将 reducer 分模块编写, 最后使用这个 API 将多个 reducer 组合成一个, 然后传入createStore
createStore
主要结构如下:
// 当触发一个action并执行reducer后有任何key对应的state值为undefined的时, 抛出一个Error function getUndefinedStateErrorMessage(key, action) { // ... } // 遍历传入的state的key, 如果组合过的reducer中不包含对应的key, 抛出warning function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) { // ... } /** * 1. 遍历执行所有的reducer, 传入`INIT`action 和 值为undefined 的state 作为初始值 , 如果返回的state值是undefined, 说明没有"设置初始state", 抛出异常 * 2. 如果第一步没问题, 会接着再调用一次reducer, 传入一个随机的action type 和 值为undefined的state作为初始值, 如果返回值为undefined, 说明没有遵守 `遇到未知的 action 时,一定要返回旧的 state`的约定, 抛出异常 **/ function assertReducerShape(reducers){ // ... } /** * 合并传入的reducer对象, 返回一个新的 reducer 函数给 `createStore` 使用。 */ export default function combineReducers(reducers) { // ... }
下面说说combineReducers这个方法
combineReducers
首先是遍历传入的 reducers 对象, 然后以同样的 key 赋值给finalReducers, 值就是对应的 reducer 函数. 调用assertReducerShape方法判断每个 reducer 是否符合基本规约 返回一个标准的 reducer 函数,接受一个 state 和 action 参数 每次调用 dispatch 时, 都会去遍历finalReducers, 获得当前 key 对应的nextState, 如果当前 state 和 nextState 不同, 将 hasChanged 标记置为 true,返回完整的 nextState,否则返回当前 state 即可.
finalReducers
assertReducerShape
nextState
function combineReducers(reducers) { 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) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { // 判断reducer是否符合规范 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 = {} // 遍历获取对应key的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 } }
从右到左来组合多个函数,并返回一个最终的函数。compose(f, g, h) 等于 (...args) => f(g(h(...args))), 不懂的话可以看一下Array.reduce的api
Array.reduce
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))) }
applyMiddleware 接收多个参数,并返回一个以 createStore 为参数的 function,此 function 经过一系列的处理之后返回了 createStore 里面的所有方法和重写的 dispatch。
applyMiddleware
function applyMiddleware(...middlewares) { // 可以去看createStore对enhancer的调用方式: return enhancer(createStore)(reducer, preloadedState) return createStore => (...args) => { // 首先创建store 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.` ) } // 调用每个中间件时传递的参数, 即对应着第一层函数的store const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 中间件的格式: (store) => next => (action) => {} // 假设现在传入的为3个中间件数组: [m1, m2, m3] // 则 chain现在为[next => (action) => { next(action); //m1 }, next => (action) => { next(action); //m2 }, next => (action) => { next(action); //m3 }] // 为了直观, 把箭头函数改写一下: [function m1(next) { return (action) { //m1 }}, function m2(next) { return (action) => { //m2 }}, function m3(next) { return (action) => { //m3 }}]=> const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 让我们回忆一下compose返回的结果: compose(f, g, h) 等于 (...args) => f(g(h(...args))) // 所以 compose(...chain) 的结果为 (...args) => m1(m2(m3(...args))) // 即 dispatch = compose(...chain)(store.dispatch) = m1(m2(m3(store.dispatch))) // 然后把最后组合结果赋值给dispatch并返回, 这样在我们调用的时候, 其实就是使用了当前这个增强后的dispatch // 我们调用dispatch({type: xxx})时, 就相当于 (action) => { next(action); //m1 }({type: xxx}) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
那么中间件的执行顺序是怎样的呢?
假设中间件最内部函数体为
{ console.log('in') next(action) console.log('out') }
中间件的执行顺序自然就是:
m1 in => m2 in => m3 in => dispatch => m3 out => m2 out => m1 out ;
执行顺序与 koa 的洋葱模型一致(见下图左)
如果我们在 dispatch 中又调用了 dispatch 会怎么样呢? 其实和在其他地方是一样的, 会从头再来一遍(如下图右),
我们顺便看一下redux-thunk的源码, 其实就有这方面的应用
redux-thunk
function createThunkMiddleware(extraArgument) { return store => next => action => { if (typeof action === 'function') { return action(store.dispatch, store.getState, extraArgument); } return next(action); }; }
redux-thunk 做的事情就是判断 action 类型是否是函数,若是,则执行 action,若不是,则继续传递 action 到下个 middleware。
bindActionCreators 函数可以生成直接触发 action 的函数; 源码很简单, 就不详细说了 惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。 源码如下:
bindActionCreators 函数可以生成直接触发 action 的函数; 源码很简单, 就不详细说了
bindActionCreators
惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。 源码如下:
function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } 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 }
Redux 源码解读
Redux 本身提供的功能有什么呢?
然后我们看一下源码的目录结构:
看起来确实也没有多少, 接下来我们一个一个看
1. createStore
大致的结构如下(省略了一些很好理解的参数判断/抛错逻辑)
2. combineReducers
主要结构如下:
下面说说
combineReducers
这个方法3. compose
4. applyMiddleware
假设中间件最内部函数体为
中间件的执行顺序自然就是:
执行顺序与 koa 的洋葱模型一致(见下图左)
如果我们在 dispatch 中又调用了 dispatch 会怎么样呢? 其实和在其他地方是一样的, 会从头再来一遍(如下图右),
我们顺便看一下
redux-thunk
的源码, 其实就有这方面的应用redux-thunk
做的事情就是判断 action 类型是否是函数,若是,则执行 action,若不是,则继续传递 action 到下个 middleware。4. bindActionCreators