关于javascript:看完Redux源码后的几个问题

37次阅读

共计 3561 个字符,预计需要花费 9 分钟才能阅读完成。

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。

正文完
 0