redux深刻進階

上一篇文章講解了redux如何使用,本篇文章將進一步深刻,從redux的源碼入手,深刻學習redux的中間件機制。
在這裏咱們會以一個redux-thunk中間件爲例,逐步分解redux的中間機制如何操做,如何執行。javascript

閒話很少說,上代碼。java

如何加載中間件

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

// create a store that has redux-thunk middleware enabled
const createStoreWithMiddleware = applyMiddleware(
  thunk
)(createStore);

const store = createStoreWithMiddleware(rootReducer);

這裏須要用到redux中提供的一個工具方法,叫作applyMiddleware,向該方法傳入你想要使用的中間件,完了以後再傳入createStore方法,
最終造成新的建立store的方法。es6

這顯然是一個裝飾器模式,經過不一樣的中間件對createStore方法進行修飾,最後造成新的createStore方法,那麼建立的store就具備這些中間件的特性,
很是出色的設計,驚喜不只在這,看了以後的代碼你就更不得不佩服做者的代碼設計能力。express

瞬間以爲別人都是碼神,而我就是碼農有木有/(ㄒoㄒ)/~~編程

中間件加載機制的實現

先來看applyMiddleware方法的實現redux

import compose from './compose';

/**
 * Creates a store enhancer that applies middleware to the dispatch method
 * of the Redux store. This is handy for a variety of tasks, such as expressing
 * asynchronous actions in a concise manner, or logging every action payload.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
export default function applyMiddleware(...middlewares) {
  return (next) => (reducer, initialState) => {
    var store = next(reducer, initialState);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch
    };
  };
}

這就是redux裏面這個方法的源碼,其中還一半是註釋有木有。。。原本覺得確定有百來行代碼的
固然這裏不得不說es6的特性提供了很是多的幫助,因此爲了省力吧es6玩透仍是灰常有必要的(更別說爲了裝X了(^__^) )數組

從這裏開始代碼就有點繞了,咱們逐行分析服務器

return (next) => (reducer, initialState) => {...}

整個applyMiddleware方法就是返回了一個方法,根據applyMiddleware方法的使用,咱們能夠知道next就是createStore方法,
由於最終咱們要返回的是一個裝飾過的createStore方法,那麼接收的參數確定是不會變,因此最終咱們調用createStoreWithMiddleware方法其實就是調用app

function (reducer, initialState) {
    var store = next(reducer, initialState); // next即爲最初的createStore方法
    // ...如下省略
}
var store = next(reducer, initialState);
var dispatch = store.dispatch;
var chain = [];

這裏沒什麼好講的,首先建立了一個store,這個store就是最原始的經過createStore建立的store,後兩行只是變量賦值異步

var middlewareAPI = {
  getState: store.getState,
  dispatch: (action) => dispatch(action)
};
chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);

這裏是關鍵,必須詳細進行講解。

首先,這邊聲明瞭一個middlewareAPI對象,這個對象包含兩個方法:

  1. getState:store中的getState方法的引用

  2. dispatch:對自己的dispatch方法進行一次封裝

而後

chain = middlewares.map(middleware => middleware(middlewareAPI));

咱們來仔細看看這行代碼,首先咱們對全部的中間件進行一個map,map結果就是調用中間件方法,將middlewareAPI做爲參數傳入,
這裏咱們拿redux-thunk中間件舉例,來看看一箇中間件是長什麼樣子的,傳入的參數又是用來幹嗎的。

export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}

redux-thunk的功能是讓action支持異步,讓咱們能夠在action中跟服務器進行交互等操做,而他的實現。。。(⊙﹏⊙)b是的,又是這麼幾行代碼。

咱們回顧以前的代碼,在map全部中間件的時候咱們調用了thunkMiddleware方法,傳入兩個方法dispatchgetState,而後返回了一個方法,
咱們大體抽象一下,應該以下:

function (next) {
  
  return function (action) {
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action)
  }
  
}

因而咱們接下去分析applyMiddleware裏面的代碼,

chain = middlewares.map(middleware => middleware(middlewareAPI));

如今咱們知道chain是一個數組,每一項是調用每一箇中間件以後的返回函數

dispatch = compose(...chain)(store.dispatch);

compose是redux裏面的一個幫助函數,代碼以下:

export default function compose(...funcs) {
  return arg => funcs.reduceRight((composed, f) => f(composed), arg);
}

~~(>_<)~~我已經不想再吐槽什麼了,

咱們看到這邊先調用了compose函數,傳入告終構後的chain數組,而後compose函數返回的也是一個函數:

function (arg) {
  return funcs.reduceRight((composed, f) => f(composed), arg);
  // funcs就是中間件數組
}

而後咱們把store.dispatch函數做爲arg傳入這個結果,這裏reduceRight能夠參考這裏
。那麼這邊獲得的結果是什麼呢?

// 假設中間件數組是[A, B, C]
// 那麼結果就是A(B(C(store.dispatch)))

再次結合redux-thunk來看,咱們假設只有一箇中間件,那麼最終的dispatch方法就是

function (action) {
  typeof action === 'function' ?
    action(dispatch, getState) :
    next(action)
}
// 這裏的next方法,就是真正的store.dispatch方法
// 這裏的dispatch是(action) => store.dispatch(action)

咱們再結合redux-thunk的使用方法來分析一下,

function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 1000);
  };
}

這是使用redux-thunk時能夠定義的異步action,咱們觸發action的時候調用的是

dispatch(incrementAsync())

incrementAsync返回的是

function (dispatch) {
  setTimeout(() => {
    // Yay! Can invoke sync or async actions with `dispatch`
    dispatch(increment());
  }, 1000);
}

這個時候咱們回想通過中間件加工的dispatch方法:

function (action) {
  typeof action === 'function' ?
    action(dispatch, getState) :
    next(action)
}
// 這裏的next方法,就是真正的store.dispatch方法
// 這裏的dispatch是(action) => store.dispatch(action)

action是一個函數,因此action === 'function' ?成立,那麼就執行action, 並把中間件接收到的dispatch方法((action) => store.dispatch(action))方法做爲參數傳入,在異步方法執行完以後再次觸發真正的action。若是action不是異步的,那麼久直接返回一個對象,這個時候action === 'function' ?不成立,就直接調用next,也就是原始的store.dispatch方法。

咱們再接着想,若是咱們有許多箇中間件,那麼沒一箇中間件的next就是下一個中間件直到最後一箇中間件調用store.dispatch爲止。

以上的代碼很是繞,建議去專研一下源碼。這麼精簡的代碼包含了很是多的函數式編程的思想,也用到了裝飾器模式的原理,不得不說:

太燒腦啦/(ㄒoㄒ)/~~

相關文章
相關標籤/搜索