上一章介紹了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)
}
}