日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
快速上手Redux

Redux是 JavaScript 狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。Redux是應(yīng)用狀態(tài)管理服務(wù)。雖然本身受到了Flux很深的影響,但是其核心概念卻非常簡單,就是 Map/Reduce中的Reduce。

從零開始實(shí)現(xiàn)一個(gè) Redux

我們先忘記 Redux 的概念,從一個(gè)例子入手,使用 create-react-app 創(chuàng)建一個(gè)項(xiàng)目: toredux。

代碼目錄: myredux/to-redux 中。

將 public/index.html 中 body 修改為如下:

image-20220124222440527

我們要實(shí)現(xiàn)的功能如上圖所示,在點(diǎn)擊按鈕時(shí),能夠修改整個(gè)應(yīng)用的字體的顏色。

修改 src/index.js 如下(代碼: to-redux/src/index1.js):

let state = {  
    color: 'blue'  
}  
//渲染應(yīng)用  
function renderApp() {  
    renderHeader();  
    renderContent();  
}  
//渲染 title 部分  
function renderHeader() {  
    const header = document.getElementById('header');  
    header.style.color = state.color;  
}  
//渲染內(nèi)容部分  
function renderContent() {  
    const content = document.getElementById('content');  
    content.style.color = state.color;  
}  
renderApp();  
//點(diǎn)擊按鈕,更改字體顏色  
document.getElementById('to-blue').onclick = function () {  
    state.color = 'rgb(0, 51, 254)';  
    renderApp();  
}  
document.getElementById('to-pink').onclick = function () {  
    state.color = 'rgb(247, 109, 132)';   
    renderApp();  
} 

這個(gè)應(yīng)用非常簡單,但是它有一個(gè)問題:state 是共享狀態(tài),但是任何人都可以修改它,一旦我們隨意修改了這個(gè)狀態(tài),就可以導(dǎo)致出錯,例如,在 renderHeader 里面,設(shè)置 state = {}, 容易造成難以預(yù)料的錯誤。

不過很多時(shí)候,我們又的確需要共享狀態(tài),因此我們可以考慮設(shè)置一些門檻,比如,我們約定,不能直接修改全局狀態(tài),必須要通過某個(gè)途經(jīng)才能修改。為此我們定義一個(gè) changeState 函數(shù),全局狀態(tài)的修改均由它負(fù)責(zé)。

//在 index.js 中繼續(xù)追加代碼  
function changeState(action) {  
    switch(action.type) { 
        case 'CHANGE_COLOR':  
            return {  
                ...state,  
                color: action.color  
            }  
        default:  
            return state;  
    }  
} 

我們約定只能通過 changeState 去修改狀態(tài),它接受一個(gè)參數(shù) action,包含 type 字段的普通對象,type 字段用于識別你的操作類型(即如何修改狀態(tài))。

我們希望點(diǎn)擊按鈕,可以修改整個(gè)應(yīng)用的字體顏色。

//在 index.js 中繼續(xù)追加代碼  
document.getElementById('to-blue').onclick = function() {  
    let state = changeState({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(0, 51, 254)'  
    });  
    //狀態(tài)修改完之后,需要重新渲染頁面  
    renderApp(state);  
}  
document.getElementById('to-pink').onclick = function() {  
    let state = changeState({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(247, 109, 132)'  
    });  
    renderApp(state);  
} 
抽離 store

盡管現(xiàn)在我們約定了如何修改狀態(tài),但是 state 是一個(gè)全局變量,我們很容易就可以修改它,因此我們可以考慮將其變成局部變量,將其定義在一個(gè)函數(shù)內(nèi)部(createStore),但是在外部還需要使用 state,因此我們需要提供一個(gè)方法 getState(),以便我們在 createStore 獲取到 state。

function createStore (state) {  
    const getState = () => state;  
    return {  
        getState  
    }  
} 

現(xiàn)在,我們可以通過 store.getState() 方法去獲取狀態(tài)(這里需要說明的是,state 通常是一個(gè)對象,因此這個(gè)對象在外部其實(shí)是可以被直接修改的,但是如果深拷貝 state 返回,那么在外部就一定修改不了,鑒于 redux 源碼中就是直接返回了 state,此處我們也不進(jìn)行深拷貝,畢竟耗費(fèi)性能)。

僅僅獲取狀態(tài)是遠(yuǎn)遠(yuǎn)不夠的,我們還需要有修改狀態(tài)的方法,現(xiàn)在狀態(tài)是私有變量,我們必須要將修改狀態(tài)的方法也放到 createStore 中,并將其暴露給外部使用。

function createStore (state) {  
    const getState = () => state;  
    const changeState = () => {  
        //...changeState 中的 code  
    }  
    return {  
        getState,  
        changeState  
    }  
} 

現(xiàn)在,index.js 中代碼變成下面這樣(to-redux/src/index2.js):

function createStore() {  
    let state = {  
        color: 'blue'  
    }  
    const getState = () => state;  
    function changeState(action) {  
        switch (action.type) {  
            case 'CHANGE_COLOR':  
                state = {  
                    ...state,  
                    color: action.color  
                }  
                return state;  
            default:  
                return state;  
        }  
    }  
    return {  
        getState,  
        changeState  
    }  
}  
function renderApp(state) {  
    renderHeader(state);  
    renderContent(state);  
}  
function renderHeader(state) {  
    const header = document.getElementById('header');  
    header.style.color = state.color;  
}  
function renderContent(state) {  
    const content = document.getElementById('content');  
    content.style.color = state.color;  
}  
document.getElementById('to-blue').onclick = function () {  
    store.changeState({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(0, 51, 254)'  
    });  
    renderApp(store.getState());  
}  
document.getElementById('to-pink').onclick = function () {  
    store.changeState({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(247, 109, 132)'  
    });  
    renderApp(store.getState());  
}  
const store = createStore();  
renderApp(store.getState()); 

盡管,我們現(xiàn)在抽離了 createStore 方法,但是顯然這個(gè)方法一點(diǎn)都不通用,state 和 changeState 方法都定義在了 createStore 中。這種情況下,其它應(yīng)用無法復(fù)用此模式。

changeState 的邏輯理應(yīng)在外部定義,因?yàn)槊總€(gè)應(yīng)用修改狀態(tài)的邏輯定然是不同的。我們將這部分邏輯剝離到外部,并將其重命名為 reducer (憋問為什么叫 reducer,問就是為了和 redux 保持一致)。reducer 是干嘛的呢,說白了就是根據(jù) action 的類型,計(jì)算出新狀態(tài)。因?yàn)樗皇窃?createStore 內(nèi)部定義的,無法直接訪問 state,因此我們需要將當(dāng)前狀態(tài)作為參數(shù)傳遞給它。如下:

function reducer(state, action) {  
    switch(action.type) {  
        case 'CHANGE_COLOR':  
            return {  
                ...state,  
                color: action.color  
            }  
        default:  
            return state;  
    }  
} 
createStore 進(jìn)化版

function createStore(reducer) {  
    let state = {  
        color: 'blue'  
    }  
    const getState = () => state;  
    //將此處的 changeState 更名為 `dispatch`  
    const dispatch = (action) => {  
        //reducer 接收老狀態(tài)和action,返回一個(gè)新狀態(tài)  
        state = reducer(state, action);  
    }  
    return {  
        getState,  
        dispatch  
    }  
} 

不同應(yīng)用的 state 定然是不同的,我們將 state 的值定義在 createStore 內(nèi)部必然是不合理的。

function createStore(reducer) {  
    let state;  
    const getState = () => state;  
    const dispatch = (action) => {  
        //reducer(state, action) 返回一個(gè)新狀態(tài)  
        state = reducer(state, action);  
    }  
    return {  
        getState,  
        dispatch  
    }  
} 

大家注意 reducer 的定義,在碰到不能識別的動作時(shí),是直接返回舊狀態(tài)的,現(xiàn)在,我們利用這一點(diǎn)來返回初始狀態(tài)。

要想 state 有初始狀態(tài),其實(shí)很簡單,咱們將初始的 state 的初始化值作為 reducer 的參數(shù)的默認(rèn)值,然后在 createStore 中派發(fā)一個(gè) reducer 看不懂的動作就可以了。這樣 getState 首次調(diào)用時(shí),可以獲取到狀態(tài)的默認(rèn)值。

createStore 進(jìn)化版2.0

function createStore(reducer) {  
    let state;  
    const getState = () => state;  
    //每當(dāng) `dispatch` 一個(gè)動作的時(shí)候,我們需要調(diào)用 `reducer` 以返回一個(gè)新狀態(tài)  
    const dispatch = (action) => {  
        //reducer(state, action) 返回一個(gè)新狀態(tài)  
        state = reducer(state, action);  
    }  
    //你要是有個(gè) action 的 type 的值是 `@@redux/__INIT__${Math.random()}`,我敬你是個(gè)狠人  
    dispatch({ type: `@@redux/__INIT__${Math.random()}` });  
    return { 
        getState,  
        dispatch  
    }  
} 

現(xiàn)在這個(gè) createStore 已經(jīng)可以到處使用了, 但是你有沒有覺得每次 dispatch 后,都手動 renderApp() 顯得很蠢,當(dāng)前應(yīng)用中是調(diào)用兩次,如果需要修改1000次 state 呢,難道手動調(diào)用 1000次 renderApp() ?

能不能簡化一下呢?每次數(shù)據(jù)變化的時(shí)候,自動調(diào)用 renderApp()。當(dāng)然我們不可能將 renderApp() 寫在 createStore() 的 dispatch 中,因?yàn)槠渌膽?yīng)用中,函數(shù)名未必叫 renderApp(),而且有可能不止要觸發(fā) renderApp()。這里可以引入 發(fā)布訂閱模式,當(dāng)狀態(tài)變化時(shí),通知所有的訂閱者。

createStore 進(jìn)化版3.0

function createStore(reducer) {  
    let state;  
    let listeners = [];  
    const getState = () => state;  
    //subscribe 每次調(diào)用,都會返回一個(gè)取消訂閱的方法  
    const subscribe = (ln) => {   
        listeners.push(ln);  
        //訂閱之后,也要允許取消訂閱。  
        //難道我訂了某本雜志之后,就不允許我退訂嗎?可怕~  
        const unsubscribe = () => {  
            listenerslisteners = listeners.filter(listener => ln !== listener);  
        }  
        return unsubscribe;  
    };  
    const dispatch = (action) => {  
        //reducer(state, action) 返回一個(gè)新狀態(tài)  
        state = reducer(state, action);  
        listeners.forEach(ln => ln());    
     }  
    //你要是有個(gè) action 的 type 的值正好和 `@@redux/__INIT__${Math.random()}` 相等,我敬你是個(gè)狠人  
    dispatch({ type: `@@redux/__INIT__${Math.random()}` });  
    return {  
        getState,  
        dispatch,  
        subscribe  
    }  
} 

至此,一個(gè)最為簡單的 redux 已經(jīng)創(chuàng)建好了,createStore 是 redux 的核心。我們來使用這個(gè)精簡版的 redux 重寫我們的代碼,index.js 文件內(nèi)容更新如下(to-redux/src/index.js):

function createStore() {  
    //code(自行將上面createStore的代碼拷貝至此處)  
}  
const initialState = {  
    color: 'blue'  
}  
function reducer(state = initialState, action) {  
    switch (action.type) {  
        case 'CHANGE_COLOR':  
            return {  
                ...state,  
                color: action.color  
            }  
        default:  
            return state;  
    }  
}  
const store = createStore(reducer);  
function renderApp(state) {  
    renderHeader(state);  
    renderContent(state);  
}  
function renderHeader(state) {  
    const header = document.getElementById('header');  
    header.style.color = state.color;  
}  
function renderContent(state) {  
    const content = document.getElementById('content');  
    content.style.color = state.color;  
}  
document.getElementById('to-blue').onclick = function () {  
    store.dispatch({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(0, 51, 254)'  
    });  
}  
document.getElementById('to-pink').onclick = function () {  
    store.dispatch({  
        type: 'CHANGE_COLOR',  
        color: 'rgb(247, 109, 132)'  
    });  
}  
renderApp(store.getState());  
//每次state發(fā)生改變時(shí),都重新渲染  
store.subscribe(() => renderApp(store.getState())); 

如果現(xiàn)在我們現(xiàn)在希望在點(diǎn)擊完 Pink 之后,字體色不允許修改,那么我們還可以取消訂閱:

const unsub = store.subscribe(() => renderApp(store.getState()));  
document.getElementById('to-pink').onclick = function () {  
    //code...  
    unsub(); //取消訂閱  
} 

順便說一句: reducer 是一個(gè)純函數(shù)(純函數(shù)的概念如果不了解的話,自行查閱資料),它接收先前的 state 和 action,并返回新的 state。不要問為什么 action 中一定要有 type 字段,這僅僅是一個(gè)約定而已(redux 就是這么設(shè)計(jì)的)

遺留問題:為什么 reducer 一定要返回一個(gè)新的 state,而不是直接修改 state 呢。歡迎在評論區(qū)留下你的答案。

前面我們一步一步推演了 redux 的核心代碼,現(xiàn)在我們來回顧一下 redux 的設(shè)計(jì)思想:

Redux 設(shè)計(jì)思想

Redux 將整個(gè)應(yīng)用狀態(tài)(state)存儲到一個(gè)地方(通常我們稱其為 store)
當(dāng)我們需要修改狀態(tài)時(shí),必須派發(fā)(dispatch)一個(gè) action( action 是一個(gè)帶有 type 字段的對象)
專門的狀態(tài)處理函數(shù) reducer 接收舊的 state 和 action ,并會返回一個(gè)新的 state
通過 subscribe 設(shè)置訂閱,每次派發(fā)動作時(shí),通知所有的訂閱者。
咱們現(xiàn)在已經(jīng)有一個(gè)基礎(chǔ)版本的 redux 了,但是它還不能滿足我們的需求。我們平時(shí)的業(yè)務(wù)開發(fā)不會像上面所寫的示例那樣簡單,那么就會有一個(gè)問題: reducer 函數(shù)可能會非常長,因?yàn)?action 的類型會非常多。這樣肯定是不利于代碼的編寫和閱讀的。

試想一下,你的業(yè)務(wù)中有一百種 action 需要處理,把這一百種情況編寫在一個(gè) reducer 中,不僅寫得人惡心,后期維護(hù)代碼的同事更是想殺人。

因此,我們最好單獨(dú)編寫 reducer,然后對 reducer 進(jìn)行合并。有請我們的 combineReducers(和 redux 庫的命名保持一致) 閃亮登場~

combineReducers

首先我們需要明確一點(diǎn):combineReducers 只是一個(gè)工具函數(shù),正如我們前面所說,它將多個(gè) reducer 合并為一個(gè) reducer。combineReducers 返回的是 reducer,也就是說它是一個(gè)高階函數(shù)。

我們還是以一個(gè)示例來說明,盡管 redux 不是非得和 react 配合,不過鑒于其與 react 配合最為適合,此處,以 react 代碼為例:

這一次除了上面的展示以外,我們新增了一個(gè)計(jì)數(shù)器功能( 使用 React 重構(gòu) ===> to-redux2):

//現(xiàn)在我們的 state 結(jié)構(gòu)如下:  
let state = {  
    theme: {  
        color: 'blue'  
    },  
    counter: {  
        number: 0  
    }  
} 

顯然,修改主題和計(jì)數(shù)器是可以分割開得,由不同的 reducer 去處理是一個(gè)更好的選擇。

store/reducers/counter.js 
負(fù)責(zé)處理計(jì)數(shù)器的state。

import { INCRENENT, DECREMENT } from '../action-types';  
export default counter(state = {number: 0}, action) {  
    switch (action.type) {  
        case INCRENENT:  
            return {  
                ...state,  
                number: state.number + action.number  
            }  
        case DECREMENT:  
            return {  
                ...state,  
                number: state.number - action.number  
            }  
        default:  
            return state; 
    }  
}    
store/reducers/theme.js 

負(fù)責(zé)處理修改主題色的state。

import { CHANGE_COLOR } from '../action-types';  
export default function theme(state = {color: 'blue'}, action) {  
    switch (action.type) {  
        case CHANGE_COLOR:  
            return {  
                ...state,  
                color: action.color  
            }  
        default:  
            return state;  
    }  
} 

每個(gè) reducer 只負(fù)責(zé)管理全局 state 中它負(fù)責(zé)的一部分。每個(gè) reducer 的 state 參數(shù)都不同,分別對應(yīng)它管理的那部分 state 數(shù)據(jù)。

import counter from './counter';  
import theme from './theme';  
export default function appReducer(state={}, action) {  
    return {  
        theme: theme(state.theme, action),  
        counter: counter(state.counter, action)  
    }  
} 

appReducer 即是合并之后的 reducer,但是當(dāng) reducer 較多時(shí),這樣寫也顯得繁瑣,因此我們編寫一個(gè)工具函數(shù)來生成這樣的 appReducer,我們把這個(gè)工具函數(shù)命名為 combineReducers。

我們來嘗試一下編寫這個(gè)工具函數(shù) combineReducers:

思路:

 combineReducers 返回 reducer
 combineReducers 的入?yún)⑹嵌鄠€(gè) reducer 組成的對象
 每個(gè) reducer 只處理全局 state 中自己負(fù)責(zé)的部分 
//reducers 是一個(gè)對象,屬性值是每一個(gè)拆分的 reducer  
export default function combineReducers(reducers) {  
    return function combination(state={}, action) {  
        //reducer 的返回值是新的 state  
        let newState = {};  
        for(var key in reducers) {  
            newState[key] = reducers[key](state[key], action);  
        }  
        return newState;  
    }  
} 

reducer 將負(fù)責(zé)返回 state 的默認(rèn)值。比如本例中,createStore 中 dispatch({type:@@redux/INIT${Math.random()}}),而傳遞給 createStore 的是 combineReducers(reducers) 返回的函數(shù) combination。

根據(jù) state=reducer(state,action),newState.theme=theme(undefined, action), newState.counter=counter(undefined, action),counter 和 theme 兩個(gè)子 reducer 分別返回 newState.theme 和 newState.counter 的初始值。

利用此 combineReducers 可以重寫 store/reducers/index.js

import counter from './counter';  
import theme from './theme';  
import { combineReducers } from '../redux';  
//明顯簡潔了許多~  
export default combineReducers({  
    counter,  
    theme  
}); 

我們寫的 combineReducers 雖然看起來已經(jīng)能夠滿足我們的需求,但是其有一個(gè)缺點(diǎn),即每次都會返回一個(gè)新的 state 對象,這會導(dǎo)致在數(shù)據(jù)沒有變化時(shí)進(jìn)行無意義的重新渲染。因此我們可以對數(shù)據(jù)進(jìn)行判斷,在數(shù)據(jù)沒有變化時(shí),返回原本的 state 即可。

combineReducers 進(jìn)化版

//代碼中省略了一些判斷,默認(rèn)傳遞的參數(shù)均是符合要求的,有興趣可以查看源碼中對參數(shù)合法性的判斷及處理  
export default function combineReducers(reducers) {  
    return function combination(state={}, action) {  
        let nextState = {};  
        let hasChanged = false; //狀態(tài)是否改變  
        for(let key in reducers) {  
            const previousStateForKey = state[key];  
            const nextStateForKey = reducers[key](previousStateForKey, action);  
            nextState[key] = nextStateForKey;  
            //只有所有的 nextStateForKey 均與 previousStateForKey 相等時(shí),hasChanged 的值才是 false  
            hasChangedhasChanged = hasChanged || nextStateForKey !== previousStateForKey;  
        }  
        //state 沒有改變時(shí),返回原對象  
        return hasChanged ? nextState : state;  
    }  
} 
applyMiddleware

官方文檔中,關(guān)于 applyMiddleware 的解釋很清楚,下面的內(nèi)容也參考了官方文檔的內(nèi)容:

日志記錄

考慮一個(gè)小小的問題,如果我們希望每次狀態(tài)改變前能夠在控制臺中打印出 state,那么我們要怎么做呢?

最簡單的即是:

當(dāng)然,這種方式肯定是不可取的,如果我們代碼中派發(fā)100次,我們不可能這樣寫一百次。既然是狀態(tài)改變時(shí)打印 state,也是說是在 dispatch 之前打印 state, 那么我們可以重寫 store.dispatch 方法,在派發(fā)前打印 state 即可。

let store = createStore(reducer);  
const next = store.dispatch; //next 的命令是為了和中間件的源碼一致  
store.dispatch = action => {  
    console.log(store.getState());  
    next(action);  
} 

崩潰信息

假設(shè)我們不僅僅需要打印 state,還需要在派發(fā)異常出錯時(shí),打印出錯誤信息。

const next = store.dispatch; //next 的命名是為了和中間件的源碼一致  
store.dispatch = action => {  
    try{  
        console.log(store.getState());  
        next(action);  
    } catct(err) {  
        console.error(err);  
    }  
} 

而如果我們還有其他的需求,那么就需要不停的修改 store.dispatch 方法,最后導(dǎo)致這個(gè)這部分代碼難以維護(hù)。

因此我們需要分離 loggerMiddleware 和 exceptionMiddleware.

let store = createStore(reducer);  
const next = store.dispatch; //next 的命名是為了和中間件的源碼一致  
const loggerMiddleware = action => {  
    console.log(store.getState());  
    next(action);  
}  
const exceptionMiddleware = action => {  
    try{  
        loggerMiddleware(action);  
    }catch(err) {  
        console.error(err);  
    }  
}  
store.dispatch = exceptionMiddleware; 

我們知道,很多 middleware 都是第三方提供的,那么 store 肯定是需要作為參數(shù)傳遞給 middleware ,進(jìn)一步改寫:

const loggerMiddleware = store => action => {  
    const next = store.dispatch;  
    console.log(store.getState());  
    next(action);  
}  
const exceptionMiddleware = store => action => {  
    try{  
        loggerMiddleware(store)(action);  
    }catch(err) {  
        console.error(err);  
    }  
}  
//使用  
store.dispatch = exceptionMiddleware(store)(action); 

現(xiàn)在還有一個(gè)小小的問題,exceptionMiddleware 中的 loggerMiddleware 是寫死的,這肯定是不合理的,我們希望這是一個(gè)參數(shù),這樣使用起來才靈活,沒道理只有 exceptionMiddleware 需要靈活,而不管 loggerMiddleware,進(jìn)一步改寫如下:

const loggerMiddleware = store => next => action => {  
    console.log(store.getState());  
    return next(action);  
}  
const exceptionMiddleware = store => next => action => {  
    try{  
        return next(action);  
    }catch(err) {  
        console.error(err);  
    }  
}  
//使用  
const next = store.dispatch;  
const logger = loggerMiddleware(store);  
store.dispatch = exceptionMiddleware(store)(logger(next)); 

現(xiàn)在,我們已經(jīng)有了通用 middleware 的編寫格式了。

middleware 接收了一個(gè) next() 的 dispatch 函數(shù),并返回一個(gè) dispatch 函數(shù),返回的函數(shù)會被作為下一個(gè) middleware 的 next()

但是有一個(gè)小小的問題,當(dāng)中間件很多的時(shí)候,使用中間件的代碼會變得很繁瑣。為此,redux 提供了一個(gè) applyMiddleware 的工具函數(shù)。

上面我們能夠看出,其實(shí)我們最終要改變的就是 dispatch,因此我們需要重寫 store,返回修改了 dispatch 方法之后的 store.

所以,我們可以明確以下幾點(diǎn):

 applyMiddleware 返回值是 store
 applyMiddleware 肯定要接受 middleware 作為參數(shù)
 applyMiddleware 要接受 store 作為入?yún)?,不過 redux 源碼中入?yún)⑹?nbsp;createStore 和 createStore 的入?yún)?,想想也是,沒有必要在外部創(chuàng)建出 store,畢竟在外部創(chuàng)建出的 store 除了作為參數(shù)傳遞進(jìn)函數(shù),也沒有其它作用,不如把 createStore 和 createStore 需要使用的參數(shù)傳遞進(jìn)來。 
//applyMiddleWare 返回 store.  
const applyMiddleware = middleware => createStore => (...args) => {  
    let store = createStore(...args);  
    let middle = loggerMiddleware(store);  
    let dispatch = middle(store.dispatch); //新的dispatch方法  
    //返回一個(gè)新的store---重寫了dispatch方法  
    return {  
        ...store,  
        dispatch  
    }  
} 

以上是一個(gè) middleware 的情況,但是我們知道,middleware 可能是一個(gè)或者是多個(gè),而且我們主要是要解決多個(gè) middleware 的問題,進(jìn)一步改寫。

//applyMiddleware 返回 store.  
const applyMiddleware = (...middlewares) => createStore => (...args) => {  
    let store = createStore(...args);  
    //順便說一句: redux 源碼中沒有直接把 store 傳遞過去,而是把 getState 和 dispatch 傳遞給了 middleware  
    let middles = middlewares.map(middleware => middleware(store));  
    //現(xiàn)在我們有多個(gè) middleware,需要多次增強(qiáng) dispatch  
    let dispatch = middles.reduceRight((prev, current) => prev(current), store.dispatch);  
    return {  
        ...store,  
        dispatch  
    }  
} 

不知道大家是不是理解了上面的 middles.reduceRight,下面為大家細(xì)致說明一下:

/*三個(gè)中間件*/  
let logger1 = store => dispatch => action => {  
    console.log('111');  
    dispatch(action);  
    console.log('444');  
}  
let logger2 = store => dispatch => action => {  
    console.log('222');  
    dispatch(action);  
    console.log('555')  
}  
let logger3 = store => dispatch => action => {  
    console.log('333');  
    dispatch(action);  
    console.log('666');  
}  
let middle1 = logger1(store);  
let middle2 = logger2(store);  
let middle3 = logger3(store);  
//applyMiddleware(logger1,logger3,logger3)(createStore)(reducer)  
//如果直接替換  
store.dispatch = middle1(middle2(middle3(store.dispatch))); 

觀察上面的 middle1(middle2(middle3(store.dispatch))),如果我們把 middle1,middle2,middle3 看成是數(shù)組的每一項(xiàng),如果對數(shù)組的API比較熟悉的話,可以想到 reduce,如果你還不熟悉 reduce,可以查看MDN文檔。

//applyMiddleware(logger1,logger3,logger3)(createStore)(reducer)  
//reduceRight 從右到左執(zhí)行  
middles.reduceRight((prev, current) => current(prev), store.dispatch);  
//第一次 prev: store.dispatch    current: middle3    
//第二次 prev: middle3(store.dispatch) current: middle2  
//第三次 prev: middle2(middle3(store.dispatch))  current: middle1  
//結(jié)果 middle1(middle2(middle3(store.dispatch))) 

閱讀過 redux 的源碼的同學(xué),可能知道源碼中是提供了一個(gè) compose 函數(shù),而 compose 函數(shù)中沒有使用 reduceRight,而是使用的 reduce,因而代
網(wǎng)站標(biāo)題:快速上手Redux
當(dāng)前網(wǎng)址:http://www.5511xx.com/article/dpdipis.html