前言:
終于好好理解了middleware。。。。
1.redux middleware提供的是位于 action 被發起之后,到達 reducer 之前的擴展點。
redux通過store.dispatch(atcion),發起一個action給store,store接收后把當前state與action一起傳給reducer,所以middleware做文章的地方就是dispatch,在原生的dispatch執行之前,先進行一些列的操作。
實現middleware的方法:下面以logger中間件為例
1.手動記錄(不可能會使用這種的吧)
let action = addTodo('Use Redux') console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState())
2.封裝dispatch,
要發起action的時候,不用 dispatch 而是用 disptchAddLog()。(把這個方法放在store里面、好像還不錯的樣子)
function dispatchAndLog(store, action) { console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState()) }
3.猴子補丁,
通過重寫store.dispatch(), (重寫之后,所使用的dispatch(), 已經不是原生的dispatch了,使用多個中間件,就是不斷改寫前一次生成的dispatch),
(注意:原生的dispatch,已經不可能找到,也就是不能單獨使用原始dispatch了)
function(store) { let next = store.dispatch store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } }
4.隱藏猴子補丁,
乍一看好像和猴子補丁沒什么卻別,但其實它把賦值的給store.dispatch的邏輯放到了中間件函數的外面,需要外面提過一個applyMiddleware輔助函數來完成插值,除此之外,真的沒什么區別。(原理還是猴子補丁)
function logger(store) { let next = store.dispatch // 我們之前的做法: // store.dispatch = function dispatchAndLog(action) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } }
多個中間件 的實現方式:
function applyMiddlewareByMonkeypatching(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() // 在每一個 middleware 中變換 dispatch 方法。 middlewares.forEach(middleware => store.dispatch = middleware(store) ) }
5.移除猴子補丁,
相對隱藏猴子不同,把middleware函數里面 let next = store.dispatch ,放到函數外面 dispatch = middleware(store)(dispatch)
function logger(store) { return function wrapDispatchToAddLogging(next) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } }
看一下多個中間件實現方式你就知道了,
是否發現:在 return 的 assign 之前, store.dispatch 都是原生的,并沒有被改變。所以中間件里面是否可以使用store.dispatch調用原生的,(如果有需要的話),然而筆者試著調用了一下,并不行,會不斷的觸發,就是已經是該改變之后的了。
// 警告:這只是一種“單純”的實現方式! // 這 *并不是* Redux 的 API. function applyMiddleware(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() let dispatch = store.dispatch middlewares.forEach(middleware => dispatch = middleware(store)(dispatch) ) return Object.assign({}, store, { dispatch }) }
logger中間件換成es6的箭頭函數更好看
const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
6.redux中是現實方法
這與 Redux 中 applyMiddleware()
的實現已經很接近了,但是有三個重要的不同之處:
-
它只暴露一個 store API 的子集給 middleware:
dispatch(action)
和getState()
。 -
它用了一個非常巧妙的方式來保證你的 middleware 調用的是
store.dispatch(action)
而不是next(action)
,從而使這個 action 會在包括當前 middleware 在內的整個 middleware 鏈中被正確的傳遞。這對異步的 middleware 非常有用。 -
為了保證你只能應用 middleware 一次,它作用在
createStore()
上而不是store
本身。因此它的簽名不是(store, middlewares) => store
, 而是(...middlewares) => (createStore) => createStore
。
總結:
redux提供了applyMiddleware(), 如果我們自定義中間件,也就變的很簡單了
function myMiddleware = (store) => (next) => (action) => { console.log('進入了我自己定義的中間件'); let result = next(action); // 執行下一步 // let result = store.dispatch(action); //不調用next,直接使用store.dispatch調用原生。然后是不行的,會陷入dispatch死循環。 return result; }
applyMiddleware會幫我們執行前面兩層函數。myMiddleware(store)(dispatch)
redux-thunk的實現也是很牛掰
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
文章列表