成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Redux:Middleware你咋就這么難

superPershing / 2905人閱讀

摘要:接下來的函數(shù)就有點難度了,讓我們一行一行來看。上面實際的含義就是將數(shù)組每一個執(zhí)行的返回值保存的數(shù)組中。需要注意的是,方法返回值并不是數(shù)組,而是形如初始值的經(jīng)過疊加處理后的操作。從而實現(xiàn)異步的。

  這段時間都在學(xué)習(xí)Redux,感覺對我來說初學(xué)難度很大,中文官方文檔讀了好多遍才大概有點入門的感覺,小小地總結(jié)一下,首先可以看一下Redux的基本流程:

  從上面的圖可以看出,簡單來說,單一的state是存儲在store中,當要對state進行更新的時候,首先要發(fā)起一個action(通過dispatch函數(shù)),action的作用就是相當于一個消息通知,用來描述發(fā)生了什么(比如:增加一個Todo),然后reducer會根據(jù)action來進行對state更新,這樣就可以根據(jù)新的state去渲染View。
  
  當然上面僅僅是發(fā)生同步Action的情況下,如果是Action是異步的(例如從服務(wù)器獲取數(shù)據(jù)),那么情況就有所不同了,必須要借助Redux的中間件Middleware。
  

Redux moddleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer

  根據(jù)官方的解釋,Redux中間件在發(fā)起一個actionaction到達reducer的之間,提供了一個第三方的擴展。本質(zhì)上通過插件的形式,將原本的action->redux的流程改變?yōu)?b>action->middleware1->middleware2-> ... ->reducer,通過改變數(shù)據(jù)流,從而實現(xiàn)例如異步Action、日志輸入的功能。
  首先我們舉例不使用中間件Middleware創(chuàng)建store:

import rootReducer from "./reducers"
import {createStore} from "redux"

let store =  createStore(rootReducer);

  那么使用中間件的情況下(以redux-logger舉例),創(chuàng)建過程如下:

import rootReducer from "./reducers"
import {createStore,applyMiddleware} from "redux"
import createLogger from "redux-logger"

const loggerMiddleware = createLogger();
let store = applyMiddleware(loggerMiddleware)(createStore)(rootReducer);

  
  那么我們不經(jīng)要問了,為什么采用了上面的代碼就可以實現(xiàn)打印日志的中間件呢?
  首先給出applyMiddleware的源碼(Redux1.0.1版本):

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
              };
           };
}

  上面的代碼雖然只有不到20行,但看懂確實是不太容易,實際上包含了函數(shù)式編程各種技術(shù),首先最明顯的使用到了柯里化(Currying),在我理解中柯里化(Currying)實際就是將多參數(shù)函數(shù)轉(zhuǎn)化為單參數(shù)函數(shù)并延遲執(zhí)行函數(shù),例如:

function add(x){
    return function(y){
        return x + y;
    }
}
var add5 = add(5);
console.log(add5(10)); // 10

  關(guān)于柯里化(Currying)更詳細的介紹可以看我之前的一篇文章從一道面試題談?wù)労瘮?shù)柯里化(Currying)。
  首先我們看applyMiddleware的總體結(jié)構(gòu):

export default function applyMiddleware(...middlewares) {            return (next)  => 
        (reducer, initialState) => {
        };
}

  哈哈,典型的柯里化(Currying),其中(...middlewares)用到了ES6中的新特性,用于將任意個中間件參數(shù)轉(zhuǎn)化為中間件數(shù)組,因此很容易看出來在該函數(shù)的調(diào)用方法就是:

let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);

  其中applyMiddleware形參和實參的對應(yīng)關(guān)系是:

形參 實參
middlewares [middleware1,middleware2]
createStore Redux原生createStore
reducer, preloadedState, enhancer 原生createStore需要填入的參數(shù)

  再看函數(shù)體:

var store = next(reducer, initialState);
var dispatch = store.dispatch;
var chain = [];
var middlewareAPI = {
    getState: store.getState,
    dispatch: (action) => dispatch(action)
};

  上面代碼非常簡單,首先得到store,并將之前的store.dispatch存儲在變量dispatch中,聲明chain,以及將middleware需要的參數(shù)存儲到變量middlewareAPI中。接下來的函數(shù)就有點難度了,讓我們一行一行來看。

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

  上面實際的含義就是將middleware數(shù)組每一個middleware執(zhí)行
middleware(middlewareAPI)的返回值保存的chain數(shù)組中。那么我們不經(jīng)要問了,中間件函數(shù)到底是怎樣的?我們提供一個精簡版的createLogger函數(shù):

export default function createLogger({ getState }) {
      return (next) => 
        (action) => {
              const console = window.console;
              const prevState = getState();
              const returnValue = next(action);
              const nextState = getState();
              const actionType = String(action.type);
              const message = `action ${actionType}`;

              console.log(`%c prev state`, `color: #9E9E9E`, prevState);
              console.log(`%c action`, `color: #03A9F4`, action);
              console.log(`%c next state`, `color: #4CAF50`, nextState);
              return returnValue;
    };
}

  可見中間件createLogger也是典型的柯里化(Currying)函數(shù)。{getState}使用了ES6的解構(gòu)賦值,createLogger(middlewareAPI))返回的(也就是數(shù)組chain存儲的是)函數(shù)的結(jié)構(gòu)是:

(next) => (action) => {
//包含getState、dispatch函數(shù)的閉包
};

  我們接著看我們的applyMiddleware函數(shù)

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

  這句是最精妙也是最有難度的地方,注意一下,這里的...操作符是數(shù)組展開,下面我們先給出Redux中復(fù)合函數(shù)compose函數(shù)的實現(xiàn)(Redux1.0.1版本):

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

  首先先明確一下reduceRight(我用過的次數(shù)區(qū)區(qū)可數(shù),所以介紹一下reducereduceRight)
  

Array.prototype.reduce.reduce(callback, [initialValue])

reduce方法有兩個參數(shù),第一個參數(shù)是一個callback,用于針對數(shù)組項的操作;第二個參數(shù)則是傳入的初始值,這個初始值用于單個數(shù)組項的操作。需要注意的是,reduce方法返回值并不是數(shù)組,而是形如初始值的經(jīng)過疊加處理后的操作。
callback分別有四個參數(shù):

accumulator:上一次callback返回的累積值

currentValue: 當前值

currentIndex: 當前值索引

array: 數(shù)組
例如:

var sum = [0, 1, 2, 3].reduce(function(a, b) {
return a + b;
}, 0);
// sum is 6

  reducereduceRight的區(qū)別就是從左到右和從右到左的區(qū)別。所以如果我們調(diào)用compose([func1,func2],store.dispatch)的話,實際返回的函數(shù)是:

//也就是當前dispatch的值
func1(func2(store.dispatch))

  勝利在望,看最后一句:

return {
    ...store,
    dispatch
};

  這里其實是ES7的用法,相當于ES6中的:

return Object.assign({},store,{dispatch:dispatch});

  或者是Underscore.js中的:

return _.extends({}, store, { dispatch: dispatch });

  懂了吧,就是新創(chuàng)建的一個對象,將store中的所有可枚舉屬性復(fù)制進去(淺復(fù)制),并用當前的dispatch覆蓋store中的dispatch屬性。所以

let store = applyMiddleware(loggerMiddleware)(createStore)(rootReducer);

中的store中的dispatch屬性已經(jīng)不是之前的Redux原生的dispatch而是類似于func1(func2(store.dispatch))這種形式的函數(shù)了,但是我們不禁又要問了,那么中間件Miffffdleware又是怎么做的呢,我們看一下之前我們提供的建議的打印日志的函數(shù):

export default function createLogger({ getState }) {
      return (next) => 
        (action) => {
              const console = window.console;
              const prevState = getState();
              const returnValue = next(action);
              const nextState = getState();
              const actionType = String(action.type);
              const message = `action ${actionType}`;

              console.log(`%c prev state`, `color: #9E9E9E`, prevState);
              console.log(`%c action`, `color: #03A9F4`, action);
              console.log(`%c next state`, `color: #4CAF50`, nextState);
              return returnValue;
    };
}

  假設(shè)一下,我們現(xiàn)在使用兩個中間件,createLoggercreateMiddleware,其中createMiddleware的函數(shù)為

export default function createMiddleware({ getState }) {
      return (next) => 
        (action) => {
        return next(action)
    };
}

調(diào)用形式為:

let store = applyMiddleware(createLogger,createMiddleware)(createStore)(rootReducer);

如果調(diào)用了store.dispatch(action),chain中的兩個函數(shù)分別是
createLoggercreateMiddleware中的

(next) => (action) => {}

部分。我們姑且命名一下chain中關(guān)于createLogger的函數(shù)叫做
func1,關(guān)于createMiddleware的函數(shù)叫做func2。那么現(xiàn)在調(diào)用
store.dispatch(action),實際就調(diào)用了(注意順序)

//這里的store.dispatch是原始Redux提供的dispatch函數(shù)
func1(func2(store.dispatch))(action)

  上面的函數(shù)大家注意之前執(zhí)行次序,首先func2(store.dispatch再是func1(args)(action)。對于func1獲得的next的實參是參數(shù)是:

(action)=>{
    //func2中的next是store.dispatch
    next(action);
}

  那么實際上func1(...)(action)執(zhí)行的時候,也就是

const console = window.console;
const prevState = getState();
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`;

console.log(`%c prev state`, `color: #9E9E9E`, prevState);
console.log(`%c action`, `color: #03A9F4`, action);
console.log(`%c next state`, `color: #4CAF50`, nextState);
return returnValue;

的時候,getState調(diào)用的閉包MiddlewareAPI中的Redux的getState函數(shù),調(diào)用next(action)的時候,會回調(diào)createMiddleware函數(shù),然后createMiddlewarenext函數(shù)會回調(diào)真正的store.dispatch(action)。因此我們可以看出來實際的調(diào)用順序是和傳入中間件順序相反的,例如:

let store = applyMiddleware(Middleware1,Middleware2,Middleware3)(createStore)(rootReducer);

實際的執(zhí)行是次序是store.dispatch->Middleware3->Middleware2->Middleware1。
  不知道大家有沒有注意到一點,

var middlewareAPI = {
    getState: store.getState,
    dispatch: (action) => dispatch(action)
};

并沒有直接使用dispatch:dispatch,而是使用了dispatch:(action) => dispatch(action),其目的是如果使用了dispatch:dispatch,那么在所有的Middleware中實際都引用的同一個dispatch(閉包),如果存在一個中間件修改了dispatch,就會導(dǎo)致后面一下一系列的問題,但是如果使用dispatch:(action) => dispatch(action)就可以避免這個問題。
  接下來我們看看異步的action如何實現(xiàn),我們先演示一個異步action creater函數(shù):

export const FETCHING_DATA = "FETCHING_DATA"; // 拉取狀態(tài)
export const RECEIVE_USER_DATA = "RECEIVE_USER_DATA"; //接收到拉取的狀態(tài)
export function fetchingData(flag) {
    return {
        type: FETCHING_DATA,
        isFetchingData: flag
    };
}

export function receiveUserData(json) {
    return {
        type: RECEIVE_USER_DATA,
        profile: json
    }
}
export function fetchUserInfo(username) {
    return function (dispatch) {
        dispatch(fetchingData(true));
        return fetch(`https://api.github.com/users/${username}`)
            .then(response => {
                console.log(response);
                return response.json();
            })
            .then(json => {
                console.log(json);
                return json;
            })
            .then((json) => {
                dispatch(receiveUserData(json))
            })
            .then(() => dispatch(fetchingData(false)));
    };
}

  上面的代碼用來從Github API中拉取名為username的用戶信息,可見首先fetchUserInfo函數(shù)會dispatch一個表示開始拉取的action,然后使用fetch函數(shù)訪問Github的API,并返回一個Promise,等到獲取到數(shù)據(jù)的時候,dispatch一個收到數(shù)據(jù)的action,最后dispatch一個拉取結(jié)束的action。因為普通的action都是一個純JavaScript Object對象,但是異步的Action卻返回的是一個function,這是我們就要使用的一個中間件:redux-thunk。
  我們給出一個類似redux-thunk的實現(xiàn):

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

這個和你之前看到的中間件很類似。如果得到的action是個函數(shù),就用dispatch和getState當作參數(shù)來調(diào)用它,否則就直接分派給store。從而實現(xiàn)異步的Action。
  Redux入門學(xué)習(xí),如果有寫的不對的地方,希望大家指正,歡迎大家圍觀我的博客:
  
  MrErHu
  SegmentFault

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81486.html

相關(guān)文章

  • Redux 進階 - react 全家桶學(xué)習(xí)筆記(二)

    摘要:在函數(shù)式編程中,異步操作修改全局變量等與函數(shù)外部環(huán)境發(fā)生的交互叫做副作用通常認為這些操作是邪惡骯臟的,并且也是導(dǎo)致的源頭。 注:這篇是17年1月的文章,搬運自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介紹了 Redux 的各項基礎(chǔ) api。接著一步一步地介紹如何與 React 進行結(jié)合,并從引入過程中遇到的各個痛點引出 ...

    Godtoy 評論0 收藏0
  • redux深入進階

    摘要:上一篇文章講解了如何使用,本篇文章將進一步深入,從的源碼入手,深入學(xué)習(xí)的中間件機制。的功能是讓支持異步,讓我們可以在中跟服務(wù)器進行交互等操作,而他的實現(xiàn)。。。 上一篇文章講解了redux如何使用,本篇文章將進一步深入,從redux的源碼入手,深入學(xué)習(xí)redux的中間件機制。在這里我們會以一個redux-thunk中間件為例,逐步分解redux的中間機制如何操作,如何執(zhí)行。 閑話不多說,...

    omgdog 評論0 收藏0
  • JavaScript之原型與原型鏈

    摘要:個人博客原文地址萬物皆對象在中除值類型之外,其他的都是對象,為了說明這點,我們舉幾個例子我們可以使用來做類型判斷除了屬于值類型之外,其他都是對象。 個人博客原文地址 萬物皆對象 在JavaScript中除值類型之外,其他的都是對象,為了說明這點,我們舉幾個例子我們可以使用typeof來做類型判斷 typeof a; // undefined typeof 1; ...

    wuyangchun 評論0 收藏0
  • Redux源碼分析

    摘要:在得到新的狀態(tài)后,依次調(diào)用所有的監(jiān)聽器,通知狀態(tài)的變更。執(zhí)行完后,獲得數(shù)組,它保存的對象是第二個箭頭函數(shù)返回的匿名函數(shù)。部分源碼利用這個屬性,所有子組件均可以拿到這個屬性。 Redux使用中的幾個點: Redux三大設(shè)計原則 Create Store Redux middleware combineReducer Provider與Connect Redux流程梳理 Redux設(shè)計特...

    renweihub 評論0 收藏0
  • Redux 中間件分析

    摘要:假設(shè)等于,其中,,是三個中間件,等于,那么可以簡化為。最終返回中的方法以及經(jīng)過中間件包裝處理過的方法。以此類推,第二個返回的就是第一個中間件的形參。根據(jù)這個的討論,在中間件頂層調(diào)用了,結(jié)果導(dǎo)致無法執(zhí)行后面的中間件。 redux 主要包含 5 個方法,分別是: createStore combineReducers bindActionCreators applyMiddleware ...

    littlelightss 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<