相信你們都知道Redux的middleware(中間件)的概念,Redux經過middleware能夠完成發送異步action(網絡請求)、打印action的日誌等功能。相對而言,Redux的store enhancer的概念,不少人並非很清楚。前端
Redux經過API createStore建立store,createStore的函數簽名以下:git
createStore(reducer, [preloadedState], [enhancer])
其中,第3個可選參數enhancer,就是指的store enhancer。github
store enhancer,能夠翻譯成store的加強器,顧名思義,就是加強store的功能。一個store enhancer,實際上就是一個高階函數,它的參數是建立store的函數(store creator),返回值是一個能夠建立功能更增強大的store的函數(enhanced store creator),這和React中的高階組件的概念很類似。redux
store enhancer 函數的結構通常以下:網絡
function enhancerCreator() { return createStore => (...args) => { // do something based old store // return a new enhanced store } }
注意,enhancerCreator是用於建立store enhancer 的函數,也就是說enhancerCreator的執行結果纔是一個store enhancer。(…args) 參數表明建立store所需的參數,也就是createStore接收的參數,實際上就是(reducer, [preloadedState], [enhancer])。app
如今,咱們來建立一個store enhancer,用於輸出發送的action的信息和state的變化:異步
// autoLogger.js // store enhancer export default function autoLogger() { return createStore => (reducer, initialState, enhancer) => { const store = createStore(reducer, initialState, enhancer) function dispatch(action) { console.log(`dispatch an action: ${JSON.stringify(action)}`); const res = store.dispatch(action); const newState = store.getState(); console.log(`current state: ${JSON.stringify(newState)}`); return res; } return {...store, dispatch} } }
autoLogger() 改變了store dispatch的默認行爲,在每次發送action先後,都會輸出日誌信息。而後在建立store上,使用autoLogger()這個store enhancer:函數
// configureStore.js import { createStore, applyMiddleware } from 'redux'; import rootReducer from 'path/to/reducers'; import autLogger from 'path/to/autoLogger'; const store = createStore( reducer, autoLogger() );
若是你瞭解redux-logger這個redux middleware,是否是感受autoLogger()的做用和它很類似呢?難道store enhancer 和 middleware 能夠實現相同的功能?確實如此。實際上,middleware的實現函數applyMiddleware自己就是一個store enhancer,applyMiddleware源碼示意以下:spa
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { // 省略 return { ...store, dispatch } } }
applyMiddleware的結構和前面提到的store enhancer的結構徹底相同,applyMiddleware(...middlewares)的執行結果就是一個store enhancer。因此,能夠用middleware實現的功能,固然也可使用store enhancer 來實現了。從applyMiddleware(...middlewares)最終的返回結構{...store, dispatch}還能夠推測出,applyMiddleware(...middlewares)這個store enhancer 主要用來修改store的dispatch方法,這也確實是middleware的做用:加強store的dispatch功能。middleware其實是在applyMiddleware(...middlewares) 這個store enhancer之上的一層抽象,applyMiddleware(...middlewares) 傳遞給每個middleware參數{getState, dispatch},middleware對dispatch方法進行增強。翻譯
當同時使用applyMiddleware(...middlewares)和其餘store enhancer時,每每能夠先使用redux提供的compose函數,將這些store enhancer組合成一個:
// configureStore.js import { createStore, applyMiddleware, compose } from 'redux'; import rootReducer from 'path/to/reducers'; import autLogger from 'path/to/autoLogger'; const enhancer = compose( applyMiddleware(...middlewares), autLogger() ); const store = createStore( reducer, enhancer );
通過compose組合後,全部的store enhancer會造成一個洋蔥模型,compose中的第一個enhancer處於洋蔥模型的最外層,最後一個enhancer處於洋蔥模型的最內層,每當發送一個action,都會通過洋蔥模型從外到內的每一層enhancer的處理,以下圖所示。由於通常咱們經過middleware處理異步action,爲保證其餘enhancer接收到的都是普通action,因此須要將applyMiddleware(...middlewares)做爲第一個store enhancer 傳給 compose,讓全部的action先通過applyMiddleware(...middlewares)的處理。
applyMiddleware(…middlewares)將middleware限制爲只能夠加強store dispatch的功能,但這只是它自身的規範限制,對於其餘store enhancer,你能夠加強store中包含的任意方法的功能,如dispatch、subscribe、getState、replaceReducer等。畢竟,store只是一個包含一些函數的普通JavaScript對象,能夠很容易的複製其中的方法,並增長新的功能。
咱們再來看一個例子,redux-localstorage, 這個store enhancer 用來自動將store中的state持久化到localStorage中。它的主要代碼以下:
// store enhancer export default function persistState(paths, config) { // 一些功能選項配置 return next => (reducer, initialState, enhancer) => { let persistedState let finalInitialState try { persistedState = deserialize(localStorage.getItem(key)) finalInitialState = merge(initialState, persistedState) } catch (e) { console.warn('Failed to retrieve initialize state from localStorage:', e) } const store = next(reducer, finalInitialState, enhancer) const slicerFn = slicer(paths) // 主要代碼 store.subscribe(function () { const state = store.getState() const subset = slicerFn(state) try { localStorage.setItem(key, serialize(subset)) } catch (e) { console.warn('Unable to persist state to localStorage:', e) } }) return store } }
這個enhancer作的事情其實很簡單,只是在建立store後,當即訂閱了store的變化,當store中的state發生變化時,將state持久化到localStorage。
由於store enhancer中,咱們能夠任意複製、改變store中的方法,因此在自定義store enhancer時,有可能會由於破壞了Redux的正常工做流,致使整個應用沒法正常工做。下面就是一個破壞性enhancer的例子:
export default function breakingEnhancer() { return createStore => (reducer, initialState, enhancer) => { const store = createStore(reducer, initialState, enhancer) function dispatch(action) { console.log(`dispatch an action: ${JSON.stringify(action)}`); console.log(`current state: ${JSON.stringify(newState)}`); return res; } return {...store, dispatch} } }
這個例子從新建立了一個dispatch方法,但在新的dispatch方法中,並無調用老的dispatch方法,將action發送出去,致使action沒法正常發送,整個應用天然也就沒法工做。因此,自定義store enhancer時,必定要注意,不要破壞了Redux的原有工做流。
歡迎關注個人公衆號:老幹部的大前端,領取21本大前端精選書籍!