1.为什么createStore中既存在currentListeners也存在nextListeners?
看过源码的同学应该理解,createStore函数为了保留store的订阅者,不仅保留了以后的订阅者currentListeners而且也保留了nextListeners。createStore中有一个外部函数ensureCanMutateNextListeners:
function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() }}
这个函数本质的作用是确保能够扭转nextListeners,如果nextListeners与currentListeners统一的话,将currentListeners做一个拷贝赋值给nextListeners,而后所有的新增订阅勾销订阅操作都会集中在nextListeners,比方咱们看订阅的函数subscribe:
function subscribe(listener) {// ...... let isSubscribed = true ensureCanMutateNextListeners() //确保能够扭转nextListeners nextListeners.push(listener) //新增订阅,将listener增加到nextListeners数组的前面 return function unsubscribe() { //返回的是一个勾销订阅的函数unsubscribe // ...... ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) //勾销订阅,删除对应地位的listener}
咱们发现订阅和解除订阅都是在nextListeners做的操作,而后每次dispatch一个action都会做如下的操作:
function dispatch(action) { try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } // 相当于currentListeners = nextListeners const listeners = currentListeners const listeners = currentListeners = nextListeners //更新listeners数组 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() //每次dispatch都会去执行listener } return action}
咱们发现在dispatch中做了const listeners = currentListeners = nextListeners,相当于更新了以后currentListeners为nextListeners,而后告诉订阅者,到这里咱们不禁要问为什么要存在这个nextListeners?
先看一下代码中的解释:
/**The subscriptions are snapshotted just before every dispatch() call.If you subscribe or unsubscribe while the listeners are being invoked, this will not have any effect on the dispatch() that is currently in progress.However, the next dispatch() call, whether nested or not, will use a more recent snapshot of the subscription list. */
解释起来就是:订阅者(subscriptions)在每次dispatch( )调用之前都是一份快照(snapshotted)。如果在listener被调用期间,进行订阅或者勾销订阅,在本次的dispatch( )过程中是不会失效的,然而在下一次的dispatch( )调用中,无论dispatch是否嵌套调用,都应用最近一次的快照订阅者列表。
用一张图来示意:
减少nextListener这个正本是为了防止在遍历listeners的过程中因为subscribe或者unsubscribe对listeners进行的批改而引起的某个listener被漏掉了。比方你在遍历到某个listener的时候,在这个listener中unsubscribe了一个在以后listener之前的listener,这个时候持续i ++的时候就会间接跳过以后listener的下一个listener,导致以后的listener没有被执行。
2.为什么Reducer中不能进行dispatch操作?
咱们晓得在reducer函数中是不能执行dispatch操作的。一方面,reducer作为计算下一次state的纯函数是不应该承当执行dispatch这样的操作。另一方面,即便你尝试在reducer中执行dispatch,也不会胜利,并且会失去"Reducers may not dispatch actions."的提醒。因为在dispatch函数就做了相干的限度:
function dispatch(action) { if (isDispatching) { //正在执行reducer的时候如果执行dispatch会抛出谬误 throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true //执行reducer之前就会把isDispatching置为true currentState = currentReducer(currentState, action) //执行reducer } finally { isDispatching = false } //...notice listener}
在执行dispatch时就会将标记位isDispatching置为true。而后如果在currentReducer(currentState, action)执行的过程中又执行了dispatch,那么就会抛出谬误('Reducers may not dispatch actions.')。之所以做如此的限度,是因为在dispatch中会引起reducer的执行,如果此时reducer中又执行了dispatch,这样就落入了一个死循环,所以就要防止reducer中执行dispatch。
3.问什么applyMiddleware中的middlewareAPI中的dispatch要用闭包包裹?
首先来看下中间件函数的构造:
export default function createMiddleware({ getState }) { return (next) => (action) => { //before //...... next(action) //after //...... };}
中间件函数外部代码执行秩序别离是:
以左图为例,如果在中间件函数中调用了dispatch( ),执行的秩序变成了:
所以给中间件函数传入的middlewareAPI中的dispatch函数是通过applyMiddleware革新过的dispatch,而不是redux原生的dispatch。所以咱们通过一个闭包包裹dispatch:
(action) => dispatch(action)
这里传递给middlewareAPI的dispatch是一个匿名函数,匿名函数通过闭包去拜访变量dispatch。前面有:
dispatch = compose(...chain, store.dispatch);
这里把变量dispatch的值扭转了,当前通过闭包拜访到的变量必定是加强的函数。
这样咱们在前面给dispatch赋值为dispatch = compose(...chain, store.dispatch);,这样只有 dispatch 更新了,middlewareAPI 中的 dispatch 利用也会发生变化。如果咱们写成:
var middlewareAPI = { getState: store.getState, dispatch: dispatch};
那中间件函数中承受到的dispatch永远只能是最开始的redux中的dispatch。