redux是一個數據狀態管理的js框架,redux把行為抽象成一個對象,把狀態抽象成一個很大的數據結構,每次用戶或者其他什么方式需要改變頁面都可以理解成對數據狀態的改變,根據出發這次改變的不同從而有各式各樣的action(行為),如何根據action(行為)來改變狀態呢?這時候就需要一個reduce,reduce必須是一個純函數,reduce是一個定義行為如何改變狀態的邏輯處理的函數,action傳入通過reduce的邏輯改變狀態,最后狀態發生改變,發出訂閱消息,觸發監聽函數的執行。思路很簡單,看文檔也很容易上手,代碼量也不大。
但是最近使用redux發現一個問題,如果同時觸發多個action的話,會頻繁觸發監聽函數的執行,比如如下代碼
(function(){ dispatch(action1); dispatch(action2) })(dispatch)
會觸發2次監聽函數,一般監聽函數回去修改視圖,但其實處理完這兩次action,在去觸發監聽函數,用戶看到的效果是一樣的,不會看到觸發第一次action和第二次action之間的過渡,但是如果能一次性把狀態跟新完,在觸發一次監聽函數,那樣的話會避免執行一些沒必要的代碼,例如代碼如下:
(function(){ dispatch(action1,action2); })(dispatch)
但是閱讀了redux源碼發現,dispatch函數只能接受一個參數,源碼如下:
function dispatch(action) { if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }
當然了如果修改源碼的會有很多種思路,第一種就是監聽還是放到事件隊列的尾部執行,如下代碼:
var nextTick=(function(){ let promise = new Promise(function (resolve,rej){ resolve() }) return function (fn){ promise.then(fn) } })() nextTick(listen)
這樣方式的話,也許會出現隊列非常多的話會延遲跟新試圖,對用戶不友好,最后我采用了最后一個辦法,讓dispatch函數可以一次處理多個action:
function dispatch(...actions) { if (actions.some((action, index, arr) => { return !isPlainObject(action) })) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // if (!isPlainObject(action)) { // throw new Error( // 'Actions must be plain objects. ' + // 'Use custom middleware for async actions.' // ) // } if (actions.some((action, index, arr) => { return typeof action.type === 'undefined' })) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // if (typeof action.type === 'undefined') { // throw new Error( // 'Actions may not have an undefined "type" property. ' + // 'Have you misspelled a constant?' // ) // } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = actions.reduce(currentReducer, currentState) // currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return actions }