Redux中間件 結合網絡上的資料一些我的理解(我好菜,菜出翔,看大佬的分享依舊看不懂)

1.中間件怎樣在react項目中觸發執行與使用

只有發送 action 的這個步驟,即store.dispatch()方法時,纔會觸發前端

  • import { applyMiddleware } from "redux";
  • const store = createStore(reducers,applyMiddleware(logger));
  • applyMiddleware(…… , ……) 能夠依次放入項目所需用到的中間件

2.createStore()分析

const store = createStore(reducer, preloadedState, enhancer)

  • reducer:即一些改變state的純函數
  • preloadesState: 是初始化的state;接受整個應用的初始狀態做爲參數
  • enhancer: store加強功能,在redux中特指applyMiddleware()方法的返回值;通常狀況下 createStore 的第三個參數 enhancer 就是 applyMiddlewave
  • 建立store,返回幾個函數,主要是dispatch,getState,subscribe,replaceReducer

上源碼

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.')
}
複製代碼

3.一個簡易logger中間件的實現示例

// 一個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;
複製代碼
  1. 中間件是一個三階函數,看他要執行幾回才能到最內部的函數代碼,就是幾階,統稱就是高階函數。
  2. 三層函數包裝:第一層傳入store(所以可使用store的方法),第二層傳入下一個中間件next,第三層傳入這次執行的action。實現了三層函數嵌套,最後返回 next
  3. 在最內層的函數實現中,須要調用next中間件,並返回結果。

4.applyMiddleware(...middlewares) 源碼分析

做用是將全部中間件組成一個數組,依次執行
全部中間件被放進了一個數組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() 方法

  • applyMiddleware 一個三階函數,是用來改寫store的dispatch方法,並把全部的中間件都compose串聯起來,經過改寫dispatch,來實現redux功能的拓展。
  • 接收一個middleware數組做爲參數,返回一個方法,就是enhancer;
  • enhancer仍然是一個方法,他接收createStore方法做爲參數,enhancer方法返回的仍是一個方法,這個方法的參數與createStore方法同樣,都包含三個參數,分別是reducer, preloadedState, enhancer;
  • 這個最終的方法的做用有2點,首先調用createStore建立store,而後就是不斷的替換store.dispatch,以達到應用中間件的目的;最終就是把原始的store.dispatch應用到全部的中間件的最核心的位置調用,其餘的插件都在原始的dispatch外層包裹着。
  • applyMiddlewave 依然使用 createStore 建立了store 對象而且返回,只是改寫了這個對象的 dispatch 方法。
默認的redux流程
    view --> dispatch --> action --> reducer --> render --> view
    
applyMiddleware封裝以後
    View -> ( mid1 -> mid2 -> …… …… ) -> reducer --> render --> view
    可是通過applyMiddleware的包裝,store裏面的被封裝,在調動action以後,執行封裝後的dispatch就會通過一系列的中間件處理,再去觸發reducer。
複製代碼

中間件的運行原理

  • 將原生的 getState 和 dispacth 做爲第一個參數傳入中間件數組,得到執行完的 chain 數組:
    chain = middlewares.map(middleware => middleware(middlewareAPI))
  • 組合串聯 middleware:
    dispatch = compose(...chain)(store.dispatch)
    compose 將全部的中間件串聯起來組成新的 dispatch

何謂柯理化---- 把一個須要傳入多個變量的函數變爲多個嵌套函數,而且內層函數會調用上層函數的變量。

柯理化函數的核心是閉包,由於閉包,因此內層函數纔可以保留父做用域中的變量。
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
複製代碼

5.compose源碼分析

//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,才能夠繼續作操做,和平時直接異步回調又有些不同

  • 參考咱們的 logger middlewave 這裏的 composed 便是咱們的 next 參數。
  • reduceRight 和 ruduce 同樣,不過 reduceRight 是從數組的右端開始執行,arg 將做爲 reduceRight 的初始值(這裏就是 store.dispatch)。假設咱們的 chain 數組爲 [f1,f2,f3]執行完畢後 dispatch 爲 dispatch = f1(f2(f3(store.dispatch)))),調用這個新的 dispatch 每一箇中間件就能依次執行了,只有最後一箇中間件會觸發 redux 原生的 dispatch,將這個 action 分發出去
  • 在applyMiddleware 中 chain數組中都是已經預置middlewareAPI參數後的二階函數。執行傳入的參數都是 形參next。
  • 經過執行compose(...chain)(store.dispatch),last是最後一箇中間件,執行並傳入 store.dispatch, 返回一個只剩一階的(action) => {}, 不過已經預置了next參數,也就是store.dispatch
  • 而後last(...args)返回的結果傳入reduceRight的回調, 對應形參是composed。
  • f是rest的最後一項, 執行並把 composed 傳入,等同於f形參中的next... 獲得的結果也是一階函數,預置的next是last(...args) ...
  • 以此類推。這樣,就造成了一個嵌套多層的語句。 相似於logger1(logger2(store.dispatch),固然這只是一個比喻。 只不過到第一個middleware的時候,是二階函數傳入next執行,獲得一階函數返回賦值給dispatch,這時的一階函數已經變成了形似這樣:
function (action) {
  console.log('logger1 start', action);
  next(action);
  console.log('logger1 end', action);
}
複製代碼

通過compose以後的dispatch執行

  • 返回的store中dispatch被修改,執行store.dispatch的時候,也就是這個函數執行.
  • 當執行到next(action)的時候,會調用已經預置的next函數,也就是第二個中間件的(action) => {},依次類推。直到最後一箇中間件,他的next函數是store.dispatch函數,執行並把action傳入。
  • 執行完最後一箇中間件的next(action),也就是初始的dispatch。next後面的代碼再執行,再逆向把中間件走一遍,直到第一個中間件執行完畢。就會出現這種效果
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
複製代碼

6. 中間件的次序也有講究,有些時候有次序要求

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前端

(咱們的宗旨是,爲了加班,爲了禿頂……,仰望大佬),但願小夥伴們加羣一塊兒學習

相關文章
相關標籤/搜索