Redux源码分析:
- Redux源码分析--Middleware(1)
- Redux源码分析--Middleware(2)
- Redux源码分析--Enhancer
- Redux源码分析--createStore(dispatch、getState)
- Redux源码分析--CreateStore(subscribe)
- Redux源码分析--compose
上一章介绍了Redux源码分析--CreateStore(getState、subscribe),这一次介绍subscribe及其注销监听事件。如果分析的有问题,请及时提醒,谢谢。
整个createStore.js代码中真正影响到store.subscribe(listener)的有以下几个:
- let currentListeners = []; let nextListeners = currentListeners;
- subscribe方法
- ensureCanMutateNextListeners方法
- dispatch方法中监听事件的循环执行
let currentListeners = []; let nextListeners = currentListeners; 好理解,就是执行createStore()时,简单的变量初始化,让currentListeners和nextListeners指针指向同一个空数组。
ensureCanMutateNextListeners
这个方法作用:当currentListeners和nextListeners指向同一个数组时,就会给nextListeners指向另外一个数组(currentListeners.slice()返回的数组)
下面介绍一下为啥需要两个变量来处理listeners监听事件,如有问题,请提醒,谢谢
为什么需要用两个变量来定义listeners监听事件呢?比如:如果只有一个变量存放listeners时,在dispatch方法中,循环执行listeners,要是在某个listener监听事件中,删除某个监听事件,这样就会可能会影响另外监听事件的执行。下面用实例来说明下这个问题:
我们先正常情况下,循环运行a/b/c方法,打印出:a b c
let a = () => {console.log('a')};
let b = () => {console.log('b')};
let c = () => {console.log('c')};
let listeners = [a, b, c];
for(let i=0; i<listeners.length; i++) {
let listener = listeners[i];
listener();
}
之后,在考虑在执行函数中,注销掉某个方法,实例如下:
let a = () => {console.log('a')};
let c = () => {console.log('c')};
let listeners = [a];
let b = () => {
if(listeners.indexOf(c) > -1) {
const index = listeners.indexOf(c)
listeners.splice(index, 1)
}
console.log('b')
};
listeners.push(b);
listeners.push(c);
for(let i=0; i<listeners.length; i++) {
let listener = listeners[i];
listener();
}
返回结果:a b
剩下的关于subscribe方法,里面的代码就比较简单了。可以说,subscribe是一个闭包函数
subscribe内部代码实现:
- 判断store.subscribe(listener)中listener是不是一个函数,如果否,则抛出一个错误“Expected the listener to be a function”
- 当isDispatching等于true(当reducer在执行的时候)的时候,执行store.subscribe(listener),抛出一个异常
- isSubscribe变量用于判断当前监听事件是否已经注销
- 执行ensureCanMutateNextListeners
- 将listener push到nextListeners里
- 返回一个函数(目的是去注销当前监听事件)
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}