只有發送 action 的這個步驟,即store.dispatch()方法時,纔會觸發前端
const store = createStore(reducer, preloadedState, enhancer)
reducer
:即一些改變state的純函數preloadesState
: 是初始化的state;接受整個應用的初始狀態做爲參數enhancer
: store加強功能,在redux中特指applyMiddleware()方法的返回值;通常狀況下 createStore 的第三個參數 enhancer 就是 applyMiddlewave上源碼
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
複製代碼
// 一個logger中間件
const logger = store => next => action =>{
console.log('prevstate',store.getState())
console.log('dispatch',action);
let result = next(action);
console.log('nextstate',store.getState());
return result;
}
function A(store) {
return function A(next) {
return function A(action) {
/*...*/;
next(action);
/*...*/;
return /*...*/;
}
}
}
export default logger;
複製代碼
做用是將全部中間件組成一個數組,依次執行
全部中間件被放進了一個數組chain,而後嵌套執行,最後執行store.dispatch。能夠看到,中間件內部(middlewareAPI)能夠拿到getState和dispatch這兩個方法react
//applyMiddleware 源碼
(middlewares) => (createStore) => (reducer, preloadedState) => {
// 第一步先建立一個store
var store = createStore(reducer, preloadedState, enhancer)
// 緩存dispatch,原store的dispatch要改寫。
var dispatch = store.dispatch
// 定義chain來存放 執行後的二階中間件
var chain = []
// middleware 柯理化的第一個參數。參照logger1的store,這裏只保留getState,和改造後的dispatch兩個方法。
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 把中間件處理一層,把getState,dispatch方法傳進去,也就是中間件柯理化第一次的store參數。
// 這樣能保證每一箇中間件的store都是同一個,柯理化的用途就是預置參數嘛。
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 串聯起全部的中間件,dispatch從新賦值,這樣調用dispatch的時候,就會穿過全部的中間件。
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
複製代碼
applyMiddleware() 方法
默認的redux流程
view --> dispatch --> action --> reducer --> render --> view
applyMiddleware封裝以後
View -> ( mid1 -> mid2 -> …… …… ) -> reducer --> render --> view
可是通過applyMiddleware的包裝,store裏面的被封裝,在調動action以後,執行封裝後的dispatch就會通過一系列的中間件處理,再去觸發reducer。
複製代碼
中間件的運行原理
何謂柯理化---- 把一個須要傳入多個變量的函數變爲多個嵌套函數,而且內層函數會調用上層函數的變量。
柯理化函數的核心是閉包,由於閉包,因此內層函數纔可以保留父做用域中的變量。
function text1(x, y) {
return x * y;
}
console.log(add(2, 3)); // 6
function text1(x) {
return function (y) {
return x * y;
}
}
console.log(add2(2)(3)); // 6
複製代碼
//compose
其實compose是函數式編程中比較重要的一個方法。上面調用compose的時候可見是一個二階函數。
const compose = (...funcs) => {
//沒有參數,那就返回一個function
if (!funcs.length) {
return arg => arg
}
//一箇中間件,返回它
if (funcs.length === 1) {
return funcs[0];
}
// 最後一個
var last = funcs[funcs.length -1];
// 複製一份,除去last
var rest = funcs.slice(0, -1);
// 返回函數,能夠接收store.dispatch。
// reduceRight 反向迭代。
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
//reduceRight遍歷介紹
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
return previousValue + currentValue;
}, 10);
//結果 10+4+3+2+1+0 = 20
複製代碼
1 經過chain = middlewares.map(middleware => middleware(middlewareAPI)),獲得如下三個中間件的狀態變化:
//A
function A(next) {
return function A(action) {
/*...*/;
next(action);
/*...*/;
return /*...*/;
}
}
// B ……
// C ……
2 再由dispatch = compose(...chain)(store.dispatch),咱們轉化下
const last = C;
const rest = [A,B]
dispatch = rest.reduceRight(
(composed, f) =>{
return f(composed)
},
last(store.dispatch)
)
3 獲得的結果
dispatch = A(B(C(store.dispatch)));
4 進一步分析,咱們獲得的結果:
dispatch = A(B(C(store.dispatch)));
//執行C(next),獲得結果
A(B(function C(action) {/*...*/;next(action);/*...*/;return /*...*/;}));
//此時的next = store.dispatch
//繼續執行B(next)
A(function B(action) {/*...*/;next(action);/*...*/;return /*...*/;});
//此時的next = function C(action) {/*...*/;next(action);/*...*/;return /*...*/;}
//繼續執行A(next)
function A(action) {/*...*/;next(action);/*...*/;return /*...*/;};
//此時的next = function B(action) {/*...*/;next(action);/*...*/;return /*...*/;}
複製代碼
一個action觸發執行順序,A(action) -> B(action) -> C(action) -> store.dispatch(action)(生產最新的 store 數據); 若是next(action)下面還有須要執行的代碼,繼續執行 C(next 後的代碼)->B(next 後的代碼)->A(next 後的代碼)git
總結:先從內到外生成新的func,而後由外向內執行。原本咱們能夠直接使用store.dispatch(action),可是咱們能夠經過中間件對action作一些處理或轉換,好比異步操做,異步回調後再執行next;這樣的設計很巧妙,只有等待next,才能夠繼續作操做,和平時直接異步回調又有些不同
function (action) {
console.log('logger1 start', action);
next(action);
console.log('logger1 end', action);
}
複製代碼
通過compose以後的dispatch執行
start logger1 Object {type: "ADD_TODO", text: "defaultText"}
start logger2 Object {type: "ADD_TODO", text: "defaultText"}
dispatch()
end logger2 Object {type: "ADD_TODO", text: "defaultText"}
end logger1 Object {type: "ADD_TODO", text: "defaultText"}
dispatch(actions) --> m1 --> next --> m2 --> next --> m3 --> (store.dispatch(action)) --> m3 --> m2 --> m1
複製代碼
const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger)
);
複製代碼
好比,logger(打印日誌的中間件)就必定要放在最後,不然輸出結果會不正確。github
[本文參考地址來自xiexin大佬]juejin.im/post/5a23b1…)編程
[本文參考地址來自誑逩蝸犇大佬]www.jianshu.com/p/dbe65d95c…)redux
[本文參考地址來自sunyongjian大佬]github.com/sunyongjian…)segmentfault
[本文參考地址來自Deot大佬]segmentfault.com/a/119000000…)數組
[本文參考地址來自yuanyuanispeak大佬]blog.csdn.net/yuanyuanisp…)promise
[本文參考地址來自劉一奇大佬]www.liuyiqi.cn/2016/02/02/…)緩存
前端react QQ羣:
788023830
----React/Redux - 地下老英雄
前端交流 QQ羣:
249620372
----FRONT-END-JS前端
(咱們的宗旨是,爲了加班,爲了禿頂……,仰望大佬),但願小夥伴們加羣一塊兒學習