redux主要工作的是以下五个文件:
- createStore.js
- applyMiddleware.js
- bindActionCreators.js 用于将传入的 actionCreator 与 dispatch 方法相结合,揉成一个新的方法
- combineReducers.js 用于将多个 reducer 合并起来
- compose.js 用于把接收到的函数从右向左进行组合
createStore
接收三个入参
- reducer
- 初始状态内容
- 指定中间件
基本流程:
-
处理没有传入初始状态的情况
-
如果enhancer不为空,就用enhancer包裹createStore
enhancer 就是异步中间件
-
定义一些内部变量
-
定义ensureCanMutateNextListeners方法
该方法确保currentListeners与nextListeners不是同一个引用
-
定义getState 该方法用于获取当前的状态
-
定义subcribe 方法,该方法用于注册listeners ,也就是订阅监听函数
-
定义dispacth 该方法用于派发action,调用reducer并触发订阅
-
定义replaceReducer,该方法用于替换reducer
-
定义observable方法 未使用到
-
将getState、subcribe、dispacth放在store对象中返回
dispatch
function dispatch(action) {
// 校验 action 的数据格式是否合法
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +'Use custom middleware for async actions.'
)
}
// 约束 action 中必须有 type 属性作为 action 的唯一标识
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +'Have you misspelled a constant?'
)
}
// 若当前已经位于 dispatch 的流程中,则不允许再度发起 dispatch(禁止套娃)
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
// 执行 reducer 前,先"上锁",标记当前已经存在 dispatch 执行流程
isDispatching = true
// 调用 reducer,计算新的 state
currentState = currentReducer(currentState, action)
} finally {
// 执行结束后,把"锁"打开,允许再次进行 dispatch
isDispatching = false
}
// 触发订阅
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
dispatch里面除了校验参数合法性之外,其实就做了两件事
- 调用reducer ,计算新的state
- 触发订阅,遍历listeners数组中的函数,并依次执行
Redux 中的“发布-订阅”模式:认识 subscribe
dispatch 中执行的 listeners 数组从订阅中来,而执行订阅需要调用 subscribe
subscribe 在订阅时只需要传入监听函数,而不需要传入事件类型。这是因为 Redux 中已经默认了订阅的对象就是“状态的变化(准确地说是 dispatch 函数的调用) ”这个事件。
工作流程:
- 调用ensureCanMutateNextListeners,确保currentListeners与nextListeners不是同一个引用
- 注册监听函数,将入参listener函数推入到nextListeners数组中
- 返回取消订阅当前listener的方法
1、subscribe 是如何与 Redux 主流程结合的呢?
在 store 对象创建成功后,通过调用 store.subscribe 来注册监听函数,也可以通过调用 subscribe 的返回函数来解绑监听函数,监听函数是用 listeners 数组来维护的;
当dispatch action 发生时,Redux 会在 reducer 执行完毕后,将 listeners 数组中的监听函数逐个执行
2、为什么会有 currentListeners 和 nextListeners 两个 listeners 数组?
要理解这个问题,我们首先要搞清楚 Redux 中的订阅过程和发布过程各自是如何处理 listeners 数组的。
订阅过程中:
createStore 的变量初始化阶段,nextListeners 会被赋值为 currentListeners,这之后两者确实指向同一个引用。
但在 subscribe 第一次被调用时,ensureCanMutateNextListeners会将 nextListeners 纠正为一个内容与 currentListeners 一致、但引用不同的新对象。
在 subscribe 的逻辑中,ensureCanMutateNextListeners 每次都会在 listener 注册前被无条件调用,用以确保两个数组引用不同。紧跟在 ensureCanMutateNextListeners 之后执行的是 listener 的注册逻辑,我们可以对应源码中看到 listener 最终会被注册到 nextListeners 数组中去:
发布过程中:
在触发订阅的过程中,currentListeners 会被赋值为 nextListeners,而实际被执行的 listeners 数组又会被赋值为 currentListeners。因此,最终被执行的 listeners 数组,实际上和当前的 nextListeners 指向同一个引用。
currentListeners 数组用于确保监听函数执行过程的稳定性
假如说不存在 currentListeners,那么也就意味着不需要 ensureCanMutateNextListeners 这个动作。
若没有 ensureCanMutateNextListeners,那么一旦在循环的过程中,listeners数组的长度发生改变,那么可能就会抛异常