国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Redux:Middleware你咋就這么難

superPershing / 2903人閱讀

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

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

  從上面的圖可以看出,簡(jiǎn)單來(lái)說(shuō),單一的state是存儲(chǔ)在store中,當(dāng)要對(duì)state進(jìn)行更新的時(shí)候,首先要發(fā)起一個(gè)action(通過(guò)dispatch函數(shù)),action的作用就是相當(dāng)于一個(gè)消息通知,用來(lái)描述發(fā)生了什么(比如:增加一個(gè)Todo),然后reducer會(huì)根據(jù)action來(lái)進(jìn)行對(duì)state更新,這樣就可以根據(jù)新的state去渲染View。
  
  當(dāng)然上面僅僅是發(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ā)起一個(gè)actionaction到達(dá)reducer的之間,提供了一個(gè)第三方的擴(kuò)展。本質(zhì)上通過(guò)插件的形式,將原本的action->redux的流程改變?yōu)?b>action->middleware1->middleware2-> ... ->reducer,通過(guò)改變數(shù)據(jù)流,從而實(shí)現(xiàn)例如異步Action、日志輸入的功能。
  首先我們舉例不使用中間件Middleware創(chuàng)建store:

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

let store =  createStore(rootReducer);

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

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)要問(wèn)了,為什么采用了上面的代碼就可以實(shí)現(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í)際上包含了函數(shù)式編程各種技術(shù),首先最明顯的使用到了柯里化(Currying),在我理解中柯里化(Currying)實(shí)際就是將多參數(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)更詳細(xì)的介紹可以看我之前的一篇文章從一道面試題談?wù)労瘮?shù)柯里化(Currying)。
  首先我們看applyMiddleware的總體結(jié)構(gòu):

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

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

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

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

形參 實(shí)參
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)
};

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

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

  上面實(shí)際的含義就是將middleware數(shù)組每一個(gè)middleware執(zhí)行
middleware(middlewareAPI)的返回值保存的chain數(shù)組中。那么我們不經(jīng)要問(wèn)了,中間件函數(shù)到底是怎樣的?我們提供一個(gè)精簡(jiǎn)版的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;
    };
}

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

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

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

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

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

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

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

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

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

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

currentValue: 當(dāng)前值

currentIndex: 當(dāng)前值索引

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í)際返回的函數(shù)是:

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

  勝利在望,看最后一句:

return {
    ...store,
    dispatch
};

  這里其實(shí)是ES7的用法,相當(dāng)于ES6中的:

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

  或者是Underscore.js中的:

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

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

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

中的store中的dispatch屬性已經(jīng)不是之前的Redux原生的dispatch而是類似于func1(func2(store.dispatch))這種形式的函數(shù)了,但是我們不禁又要問(wèn)了,那么中間件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)在使用兩個(gè)中間件,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中的兩個(gè)函數(shù)分別是
createLoggercreateMiddleware中的

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

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

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

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

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

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

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

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

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

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

并沒(méi)有直接使用dispatch:dispatch,而是使用了dispatch:(action) => dispatch(action),其目的是如果使用了dispatch:dispatch,那么在所有的Middleware中實(shí)際都引用的同一個(gè)dispatch(閉包),如果存在一個(gè)中間件修改了dispatch,就會(huì)導(dǎo)致后面一下一系列的問(wèn)題,但是如果使用dispatch:(action) => dispatch(action)就可以避免這個(gè)問(wèn)題。
  接下來(lái)我們看看異步的action如何實(shí)現(xiàn),我們先演示一個(gè)異步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)));
    };
}

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/81486.html

相關(guān)文章

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

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

    Godtoy 評(píng)論0 收藏0
  • redux深入進(jìn)階

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

    omgdog 評(píng)論0 收藏0
  • JavaScript之原型與原型鏈

    摘要:個(gè)人博客原文地址萬(wàn)物皆對(duì)象在中除值類型之外,其他的都是對(duì)象,為了說(shuō)明這點(diǎn),我們舉幾個(gè)例子我們可以使用來(lái)做類型判斷除了屬于值類型之外,其他都是對(duì)象。 個(gè)人博客原文地址 萬(wàn)物皆對(duì)象 在JavaScript中除值類型之外,其他的都是對(duì)象,為了說(shuō)明這點(diǎn),我們舉幾個(gè)例子我們可以使用typeof來(lái)做類型判斷 typeof a; // undefined typeof 1; ...

    wuyangchun 評(píng)論0 收藏0
  • Redux源碼分析

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

    renweihub 評(píng)論0 收藏0
  • Redux 中間件分析

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

    littlelightss 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<