相信大家都知道Redux的middleware(中間件)的概念,Redux通過middleware可以完成發(fā)送異步action(網(wǎng)絡(luò)請求)叹括、打印action的日志等功能算墨。相對而言,Redux的store enhancer的概念汁雷,很多人并不是很清楚净嘀。
1. 基本概念及使用
Redux通過API createStore創(chuàng)建store,createStore的函數(shù)簽名如下:
createStore(reducer, [preloadedState], [enhancer])
其中侠讯,第3個可選參數(shù)enhancer挖藏,就是指的store enhancer。
store enhancer厢漩,可以翻譯成store的增強器膜眠,顧名思義,就是增強store的功能。一個store enhancer宵膨,實際上就是一個高階函數(shù)架谎,它的參數(shù)是創(chuàng)建store的函數(shù)(store creator),返回值是一個可以創(chuàng)建功能更加強大的store的函數(shù)(enhanced store creator)柄驻,這和React中的高階組件的概念很相似狐树。
store enhancer 函數(shù)的結(jié)構(gòu)一般如下:
function enhancerCreator() {
return createStore => (...args) => {
// do something based old store
// return a new enhanced store
}
}
注意,enhancerCreator是用于創(chuàng)建store enhancer 的函數(shù)鸿脓,也就是說enhancerCreator的執(zhí)行結(jié)果才是一個store enhancer抑钟。(…args) 參數(shù)代表創(chuàng)建store所需的參數(shù),也就是createStore接收的參數(shù)野哭,實際上就是(reducer, [preloadedState], [enhancer])在塔。
現(xiàn)在,我們來創(chuàng)建一個store enhancer拨黔,用于輸出發(fā)送的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的默認行為蛔溃,在每次發(fā)送action前后,都會輸出日志信息篱蝇。然后在創(chuàng)建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()
);
2. 與middleware (中間件)的關(guān)系
如果你了解redux-logger這個redux middleware零截,是不是感覺autoLogger()的作用和它很相似呢麸塞?難道store enhancer 和 middleware 可以實現(xiàn)相同的功能?確實如此涧衙。實際上哪工,middleware的實現(xiàn)函數(shù)applyMiddleware本身就是一個store enhancer,applyMiddleware源碼示意如下:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 省略
return {
...store,
dispatch
}
}
}
applyMiddleware的結(jié)構(gòu)和前面提到的store enhancer的結(jié)構(gòu)完全相同弧哎,applyMiddleware(...middlewares)的執(zhí)行結(jié)果就是一個store enhancer雁比。所以,可以用middleware實現(xiàn)的功能撤嫩,當然也可以使用store enhancer 來實現(xiàn)了偎捎。從applyMiddleware(...middlewares)最終的返回結(jié)構(gòu){...store, dispatch}還可以推測出,applyMiddleware(...middlewares)這個store enhancer 主要用來修改store的dispatch方法序攘,這也確實是middleware的作用:增強store的dispatch功能鸭限。middleware實際上是在applyMiddleware(...middlewares) 這個store enhancer之上的一層抽象,applyMiddleware(...middlewares) 傳遞給每一個middleware參數(shù){getState, dispatch}两踏,middleware對dispatch方法進行加強败京。
當同時使用applyMiddleware(...middlewares)和其他store enhancer時,往往可以先使用redux提供的compose函數(shù)梦染,將這些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
);
經(jīng)過compose組合后朴皆,所有的store enhancer會形成一個洋蔥模型,compose中的第一個enhancer處于洋蔥模型的最外層泛粹,最后一個enhancer處于洋蔥模型的最內(nèi)層遂铡,每當發(fā)送一個action,都會經(jīng)過洋蔥模型從外到內(nèi)的每一層enhancer的處理晶姊,如下圖所示扒接。因為一般我們通過middleware處理異步action,為保證其他enhancer接收到的都是普通action们衙,所以需要將applyMiddleware(...middlewares)作為第一個store enhancer 傳給 compose钾怔,讓所有的action先經(jīng)過applyMiddleware(...middlewares)的處理。
applyMiddleware(…middlewares)將middleware限制為只可以增強store dispatch的功能蒙挑,但這只是它自身的規(guī)范限制宗侦,對于其他store enhancer,你可以增強store中包含的任意方法的功能忆蚀,如dispatch矾利、subscribe、getState馋袜、replaceReducer等男旗。畢竟,store只是一個包含一些函數(shù)的普通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做的事情其實很簡單师痕,只是在創(chuàng)建store后,立即訂閱了store的變化而账,當store中的state發(fā)生變化時胰坟,將state持久化到localStorage。
3. 破壞性enhancer
因為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}
}
}
這個例子重新創(chuàng)建了一個dispatch方法锯茄,但在新的dispatch方法中厢塘,并沒有調(diào)用老的dispatch方法茶没,將action發(fā)送出去,導致action無法正常發(fā)送晚碾,整個應用自然也就無法工作抓半。所以,自定義store enhancer時格嘁,一定要注意笛求,不要破壞了Redux的原有工作流。
歡迎關(guān)注我的公眾號:老干部的大前端糕簿,領(lǐng)取21本大前端精選書籍探入!