日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

的源码管理器中有感叹图标_玄说前端面试层层解析—关于 redux 的源码

發(fā)布時間:2024/4/14 HTML 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 的源码管理器中有感叹图标_玄说前端面试层层解析—关于 redux 的源码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?前端面試精講系列,此篇文章是 redux 系列的第二篇

上一篇關(guān)于面試題的文章,請看:?玄說前端面試層層提問—關(guān)于 redux 的面試題

????面試題中,有很多需要清楚 redux 內(nèi)部究竟是怎么樣的,今天就一起來看個明白!

首先,你需要知道幾件事情:

1.redux 和 react 是沒有任何關(guān)系的,redux 只是一個狀態(tài)管理器,它也可以用到 Vue 里
2.connect 是屬于 react-redux 的,react-redux 是連接 redux 和 react 的一個庫

今日解析——Redux源碼

概括一點:redux 就是一個狀態(tài)管理器,那到底什么是狀態(tài)管理器呢?

1|實現(xiàn)一個簡單的狀態(tài)管理器

比如計數(shù)器中的 number,如下代碼:

let state = {
number: 1
};

使用狀態(tài)的時候通過state.number,比如修改狀態(tài)的時候state.number=2, 這樣修改 number 會有一個問題,使用到 number 的地方收不到通知,這里我們使用發(fā)布訂閱模式來解決這個問題。
代碼這樣處理:

let state = {
number: 1
};let listeners = [];/**訂閱*/function subscribe(listener) {listeners.push(listener);
}/**改變number的時候*/function changeNumber(number) {state.number = number;/**通知所有訂閱者*/for (let i = 0; i < listeners.length; i++) {const listener = listeners[i];listener();
}
}

這個時候,咱們先來嘗試一下這個計數(shù)狀態(tài)管理器。

/**訂閱*/subscribe(() => {console.log(state.number);
});/**通過changeNumber改變*/changeNumber(2);

現(xiàn)在可以觀察到,修改 number 的時候,訂閱的方法會觸發(fā),打印出相應(yīng)的 number 的值。但是現(xiàn)在有幾個問題咱們需要面對一下:

1.這個狀態(tài)管理器只能管理 number,不能通用
2.公共的代碼沒有封裝起來

咱們把公用的代碼封裝起來:

/** * initState 初始狀態(tài) */const createStore = function(initState) {let state = initState;let listeners = [];/**訂閱*/function subscribe(listener) {listeners.push(listener);
}function changeState(newState) {
state = newState;/**通知*/for (let i = 0; i < listeners.length; i++) {const listener = listeners[i];listener();
}
}/**獲取當(dāng)前狀態(tài)*/function getState() {return state;
}/**返回所有封裝的方法*/return { subscribe, changeState, getState };
};

咱們現(xiàn)在來試試這個狀態(tài)管理來管理多個狀態(tài) counter 和 info ;

/**初始數(shù)據(jù)*/let initState = {
counter: {
count: 1
},
info: {
name: "",
description: ""
}
};/**初始化store,傳入初始狀態(tài)*/let store = createStore(initState);/**訂閱*/store.subscribe(() => {let state = store.getState();console.log(`${state.info.name}:${state.info.description}`);
});store.subscribe(() => {let state = store.getState();console.log(state.counter.count);
});/**通過changeState方法改變數(shù)據(jù)info*/store.changeState({...store.getState(),
info: {
name: "玄說前端",
description: "專注前端的公眾號"
}
});/**通過changeState方法改變數(shù)據(jù)counter*/store.changeState({...store.getState(),
counter: {
count: 2
}
});

到目前,咱們已經(jīng)完成了一個簡單的狀態(tài)管理器。 在 createStore 中,提供了三個方法,改變狀態(tài)的方法 changeState,獲取當(dāng)前狀態(tài)的方法 getState,訂閱狀態(tài)發(fā)生改變的方法 subscribe

2|實現(xiàn)一個有計劃的狀態(tài)管理器

很多同學(xué)會非常迷惑,這個有計劃是什么意思?且聽我細(xì)細(xì)道來。
我們基于上面的狀態(tài)管理器來實現(xiàn)一個自增,自減的計數(shù)器。

let initState = {
count: 0
};let store = createStore(initState);/**監(jiān)聽*/store.subscribe(() => {let state = store.getState();console.log(state.count);
});/*自增===>功能正常*/store.changeState({
count: store.getState().count + 1
});/*自減===>功能正常*/store.changeState({
count: store.getState().count - 1
});/*此處我這么來一下子,一臉懵逼*/store.changeState({
count: "玄說"
});

這個代碼中,有一個問題,就是 count 被隨意改成了字符串玄說,我們對于 count 的修改沒有任何約束,它可以被改成任何值,任何地方都可以修改。這樣在程序就變得不可維護了。
此時我們需要約束 count,不允許隨意修改 count 的值,只允許 count 自增或者自減。

兩步來解決這個問題:

1.初始化 store 的時候,讓他知道我們的修改計劃是什么,制定一個 state 的修改計劃。
2.修改 store.changeState 方法,讓他在修改 state 的時候,按照我們的計劃來修改

這樣我們來制定一個 state 的修改計劃,命名為 plan 函數(shù),它接受現(xiàn)在的 state 和一個我們需要它按照修改的計劃名稱 action,返回一個改變之后的 state。

/*注意:action = {type:'',other:''}, action 必須有一個 type 屬性,表明我們的計劃*/function plan(state, action) {switch (action.type) {case "INCREMENT":return {...state,
count: state.count + 1
};case "DECREMENT":return {...state,
count: state.count - 1
};default:return state;
}
}

現(xiàn)在已經(jīng)創(chuàng)建好了一個按照意愿來修改的計劃,把計劃告訴 store,store.changeState 就會按照計劃來修改。

修改一下之前的createStore

/*增加一個參數(shù) plan*/const createStore = function(plan, initState) {let state = initState;let listeners = [];function subscribe(listener) {listeners.push(listener);
}/*給changeState傳入要修改的計劃*/function changeState(action) {/*按照計劃來修改 state*/
state = plan(state, action);for (let i = 0; i < listeners.length; i++) {const listener = listeners[i];listener();
}
}function getState() {return state;
}return {
subscribe,
changeState,
getState
};
};

現(xiàn)在再來試一下行的 createStore 實現(xiàn)的自增和自減:

let initState = {
count: 0
};/*傳入一開始定義的plan*/let store = createStore(plan, initState);/**監(jiān)聽*/store.subscribe(() => {let state = store.getState();console.log(state.count);
});/*自增*/store.changeState({
type: "INCREMENT"
});/*自減*/store.changeState({
type: "DECREMENT"
});/*傳入無效的計劃或者值類型,不會修改原state*/store.changeState({
count: "abc"
});

這樣就實現(xiàn)了一個有計劃的狀態(tài)管理器;
到這一步基本的模型就已經(jīng)實現(xiàn)了,把 plan 的名字修改成 reducer,changeState 修改成 dispatch,這樣和 redux 的叫法就一樣了。

3|關(guān)于reducer的處理

但是現(xiàn)在還存在一個問題,reducer 是一個計劃函數(shù),接受的是老的 state,一般在項目中有大量 state,每一個 state 的修改,都需要計劃函數(shù),如果所有的計劃都寫在一個 reducer 函數(shù)里面,這樣就顯得很繁雜,也不好理解。
我們可以按照組件的維度來拆分出多個 reducer 函數(shù),然后通過一個合并函數(shù),把所有的 reducer 合并到一起。比如現(xiàn)在有兩個 state,counter 和 info。

let state = {
counter: {
count: 1
},
info: {
name: "玄說前端",
description: "前端公眾號"
}
};

拆分成兩個 reducer,counterReducer 和 infoReducer。

/** * counterReducer *///這個reducer需要接受的數(shù)據(jù)是state狀態(tài)里面的state.counter!!!!!function counterReducer(state, action) {switch (action.type) {case "INCREMENT":return {
count: state.count + 1
};case "DECREMENT":return {...state,
count: state.count - 1
};default:return state;
}
}/** * infoReducer *///這個reducer需要接受的數(shù)據(jù)是state狀態(tài)里面的state.info!!!!!function infoReducer(state, action) {switch (action.type) {case "SET_NAME":return {...state,
name: action.name
};case "SET_DESCRIPTION":return {...state,
description: action.description
};default:return state;
}
}

現(xiàn)在把兩個 reducer 通過一個函數(shù)把他合并起來,我們把這個函數(shù)叫做 combineReducers,一般它是這么用的,傳入一個對象,對象包含所有的 reducer,它的 key 值對應(yīng) state 的 key 值是一樣的:

const reducer = combineReducers({
counter: counterReducer,
info: infoReducer
});

基于用法,需要返回一個合并的 reducer,執(zhí)行完成之后,傳入 state 狀態(tài)和計劃,可以返回一個新的 state,我們來實現(xiàn)以下:

/**傳入一個包含多個reducer的對象*/function combineReducers(reducers) {/* 獲取傳入對象所有的key,reducerKeys = ['counter', 'info']*/const reducerKeys = Object.keys(reducers);/*返回一個合并后的新的reducer函數(shù),接受state和action參數(shù)*/return function combination(state = {}, action) {/*初始化一個新的state*/const nextState = {};/*遍歷循環(huán)執(zhí)行所有的reducer,整合所有的state為一個新的*/for (let i = 0; i < reducerKeys.length; i++) {const key = reducerKeys[i];/*獲取當(dāng)前循環(huán)的reducer*/const reducer = reducers[key];/*傳入的reducer對象的key和總的state的key一樣。所以通過key值,獲取reducer對應(yīng)要改變的state*/const previousStateSingle = state[key];/*執(zhí)行 當(dāng)前循環(huán)的reducer,生成新的state*/const nextStateSingle = reducer(previousStateSingle, action);/**每一個新的state通過key,生成新的總的state*/
nextState[key] = nextStateSingle;
}return nextState;
};
}

至此,我們已經(jīng)完成了 combineReducers,來試驗一下:

const reducer = combineReducers({
counter: counterReducer,
info: InfoReducer
});let state = {
counter: {
count: 1
},
info: {
name: "玄說前端",
description: "前端公眾號"
}
};/**生成store*/let store = createStore(reducer, initState);/**監(jiān)聽修改*/store.subscribe(() => {/**獲取新的state*/let { counter, info } = store.getState();console.log(counter.count, info.name, info.description);
});/*自增*/store.dispatch({
type: "DECREMENT"
});/*修改 description*/store.dispatch({
type: "SET_DESCRIPTION",
description: "我愛看玄說前端"
});

至此,reducer 拆分邏輯已經(jīng)完成,在業(yè)務(wù)中只需要每個組件管理自己的狀態(tài),后面再通過合并就好。

4|關(guān)于state的整合

就目前來看還存在一個問題(怎么特么這么多問題呀,我也沒有辦法,層層深入唄??),state 所有的還寫在一起的,這樣每個組件的業(yè)務(wù)管理 state 還是有點點難維護,基于上面對于 reducer 按照組件的維度來拆分的邏輯,我們也在這個維度來做拆掉。


在業(yè)務(wù)中的時候這樣寫:

/**把每一個reducer所有修改的狀態(tài),單獨和這個reducer寫在一起*//**比如上面的counter*///單個的statelet initState = {
count: 1
};//所對應(yīng)的reducerfunction counterReducer(state, action) {/*注意:如果參數(shù) state 沒有初始值,那就給他初始值!!*/if (!state) {
state = initState;
}switch (action.type) {case "INCREMENT":return {
count: state.count + 1
};default:return state;
}
}

所有單個的狀態(tài) state,需要在一個地方我們把它整合成一個,這個最好的地方就是在 createStore 里面:

const createStore = function(reducer, initState) {let state = initState;let listeners = [];function subscribe(listener) {listeners.push(listener);
}function dispatch(action) {
state = reducer(state, action);for (let i = 0; i < listeners.length; i++) {const listener = listeners[i];listener();
}
}function getState() {return state;
}/**只是在此處增加了一行,通過一個不匹配任何計劃的 type,來獲取初始值*/dispatch({ type: Symbol() });return {
subscribe,
dispatch,
getState
};
};

這樣在 createStore 被調(diào)用的時候,不匹配任何 action,每一個 reducer 就會返回初始化的 state,合并 reducer 的時候,就把所有的 state 整合到了一起。 基于上面的 reducer,來試試下面的這個情況:

/*不傳入初始化state */const store = createStore(reducer);/*獲取state*/console.log(store.getState());

到目前為止,redux 里面基本的 reducer,createStore,combineReducers 等方法已經(jīng)實現(xiàn)的差不多了。但是還有一個特別重要的東西—中間件

5|中間件的構(gòu)成

什么是中間件?

  • 將具體業(yè)務(wù)和底層邏輯解耦的組件。

  • 數(shù)據(jù)從底層到應(yīng)用端的中轉(zhuǎn)站。

  • 有點懵逼??!!!!
    在 redux 中,中間件就是對 dispatch 的重寫或者說擴展,增強 dispatch 的功能,redux 的 dispatch 是發(fā)送一個計劃給 reducer 讓它來改變 state 狀態(tài)的,那么中間件的加入就是在 disptach 發(fā)送計劃的過程中,還能讓他做點其他的事情。在這個維度上做的擴展就是中間件的作用。

    記錄日志的中間件目前有一個需求,需要在每次修改 state 的時候,記錄修改前的 state,修改后的 state,為什么修改,這個時候就需要對 dispatch 來做擴展了:

    const store = createStore(reducer);/*暫存之前的dispatch函數(shù)*/const next = store.dispatch;/**重寫store.dispatch*/store.dispatch = action => {console.log("當(dāng)前state", store.getState());console.log("修改原因", action);//調(diào)用之前的dispatchnext(action);console.log("修改后的state", store.getState());
    };

    執(zhí)行一下:

    store.dispatch({
    type: "INCREMENT"
    });

    結(jié)果:

    當(dāng)前state { counter: { count: 1 } }
    action { type: 'INCREMENT' }2
    修改后的state { counter: { count: 2 } }

    這樣,日志的收集就已經(jīng)做好了。

    記錄異常的中間件程序中,一般都會有記錄異常原因的需求,這個時候也需要擴展一下 dispatch

    const store = createStore(reducer);const next = store.dispatch;store.dispatch = action => {try {next(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };

    多個中間件功能都需要現(xiàn)在記錄日志和異常的功能都需要,可以這么寫,兩個函數(shù)合起來:

    store.dispatch = action => {try {console.log("當(dāng)前state", store.getState());console.log("修改原因", action);//調(diào)用之前的dispatchnext(action);console.log("修改后的state", store.getState());
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };

    完美,漂亮,多么美麗?????!!!

    這個時候 SB 產(chǎn)品跑過來說,我需要在點擊某個按鈕的時候,dispatch 的時候,看看當(dāng)前的時間時間是多少,那就繼續(xù)改唄,5 個需求改五次原有的 dispatch 函數(shù)??,這樣后面 dispatch 會變得特別龐大,維護起來特別困難,所以這樣是不行的!

    那就采取一個來實現(xiàn)多個中間件融合的方式:

    第一步.把打印日志的中間件提取出來叫做 loggerMiddleware

    const store = createStore(reducer);const next = store.dispatch;/**提取*/const loggerMiddleware = action => {console.log("this state", store.getState());console.log("action", action);next(action);console.log("next state", store.getState());
    };/**重寫*/store.dispatch = action => {try {loggerMiddleware(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };

    第二步.把錯誤異常的中間件提取出來

    const exceptionMiddleware = action => {try {/*next(action)*/loggerMiddleware(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };store.dispatch = exceptionMiddleware;

    第三步,此時 exceptionMiddleware 錯誤異常的中間件里面寫定了 loggerMiddleware 日志處理的中間件,這肯定是不行,需要變成一個動態(tài)的中間件,我們通過函數(shù)傳參來解決這個問題

    const exceptionMiddleware = next => action => {try {/*loggerMiddleware(action);*/next(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };/*loggerMiddleware 變成參數(shù)傳進去*/store.dispatch = exceptionMiddleware(loggerMiddleware);

    第四步,loggerMiddleware 里面的 next 現(xiàn)在是等于 store.dispatch,導(dǎo)致 loggerMiddleware 里面無法擴展別的中間件了!我們這里把 next 寫成動態(tài)的,通過函數(shù)來傳遞。

    const loggerMiddleware = next => action => {console.log("this state", store.getState());console.log("action", action);next(action);console.log("next state", store.getState());
    };

    此時,我們摸索出來了一個比較不錯的中間件合作模式:

    const store = createStore(reducer);const next = store.dispatch;const loggerMiddleware = next => action => {console.log("this state", store.getState());console.log("action", action);next(action);console.log("next state", store.getState());
    };const exceptionMiddleware = next => action => {try {next(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };/**通過一層層的執(zhí)行*/store.dispatch = exceptionMiddleware(loggerMiddleware(next));

    但是現(xiàn)在會有個問題,因為中間件很多都是第三方擴展的,屬于外部文件,比如 loggerMiddleware 中包含了變量 store,此時外部文件是沒有這個變量的,所以也需要把 store 作為參數(shù)傳進來,這樣中間件的模式就變成了這樣:

    const store = createStore(reducer);const next = store.dispatch;const loggerMiddleware = store => next => action => {console.log("this state", store.getState());console.log("action", action);next(action);console.log("next state", store.getState());
    };const exceptionMiddleware = store => next => action => {try {next(action);
    } catch (err) {console.error("錯誤報告: ", err);
    }
    };/**傳入store,生成包含了store變量的中間件 */const logger = loggerMiddleware(store);const exception = exceptionMiddleware(store);/**依次調(diào)用,傳入中間件*/store.dispatch = exception(logger(next));

    到這里為止,我們真正的實現(xiàn)了兩個可以獨立的中間件啦!
    但是要記住上面,咱們還有一個產(chǎn)品提出的需求,在 dispatch 的時候,打印日志前,記錄時間,我們來實現(xiàn)一下:

    const timeMiddleware = store => next => action => {console.log("time", new Date().getTime());next(action);
    };

    如同上面的方法,調(diào)用一下試試

    const time = timeMiddleware(store);store.dispatch = exception(time(logger(next)));

    6|優(yōu)化中間件的使用方式

    上面實現(xiàn)的中間件使用方式不是特別友好,需要一層一層傳入執(zhí)行:

    import loggerMiddleware from "./middlewares/loggerMiddleware";import exceptionMiddleware from "./middlewares/exceptionMiddleware";import timeMiddleware from "./middlewares/timeMiddleware";const store = createStore(reducer);const next = store.dispatch;const logger = loggerMiddleware(store);const exception = exceptionMiddleware(store);const time = timeMiddleware(store);//層層傳遞執(zhí)行store.dispatch = exception(time(logger(next)));

    既然知道了三個中間件,其他的細(xì)節(jié)是不是可以封裝起來呢?咱們希望使用的時候是這樣的:

    /*傳入多個中間件,接收舊的 createStore,返回新的 createStore*/const newCreateStore = applyMiddleware(
    exceptionMiddleware,
    timeMiddleware,
    loggerMiddleware
    )(createStore);/*返回了一個 dispatch 被重寫過的 store*/const store = newCreateStore(reducer);

    內(nèi)部封裝邏輯在 applyMiddleware 中實現(xiàn):

    /**接受所有的中間件*/const applyMiddleware = function(...middlewares) {/*返回一個重寫createStore的方法,接受之前的createStore*/return function rewriteCreateStoreFunc(oldCreateStore) {/*返回重寫后新的 createStore*/return function newCreateStore(reducer, initState) {/*用之前的createStore生成store*/const store = oldCreateStore(reducer, initState);/* * 給每個 middleware 傳下store, * 相當(dāng)于 const logger = loggerMiddleware(store); *//* * 返回傳入store執(zhí)行之后的所有中間件 * const chain = [exception, time, logger] */const chain = middlewares.map(middleware => middleware(store));let dispatch = store.dispatch;/* 實現(xiàn) exception(time((logger(dispatch))))*/chain.reverse().map(middleware => {
    dispatch = middleware(dispatch);
    });/*2. 重寫 dispatch*/store.dispatch = dispatch;return store;
    };
    };
    };

    基于這一步中間件合作改造的工作已經(jīng)差不多了,但是還有一些小問題,有了兩個 createStore:

    /*沒有中間件的 createStore*/import { createStore } from "./redux";const store = createStore(reducer, initState);
    /*有中間件的 createStore*/const rewriteCreateStoreFunc = applyMiddleware(
    exceptionMiddleware,
    timeMiddleware,
    loggerMiddleware
    );const newCreateStore = rewriteCreateStoreFunc(createStore);const store = newCreateStore(reducer, initState);//這里會有newCreateStore和createStore這兩個,這樣對于用戶而言,是不好區(qū)分的。

    這里在修改一下 createStore:

    const createStore = (reducer, initState, rewriteCreateStoreFunc) => {/*如果有 rewriteCreateStoreFunc,那就采用新的 createStore */if (rewriteCreateStoreFunc) {const newCreateStore = rewriteCreateStoreFunc(createStore);return newCreateStore(reducer, initState);
    }/*否則按照正常的流程走*/
    };

    最終的用法:

    const rewriteCreateStoreFunc = applyMiddleware(
    exceptionMiddleware,
    timeMiddleware,
    loggerMiddleware
    );const store = createStore(reducer, initState, rewriteCreateStoreFunc);

    7|增加退訂事件

    修改一下 subscribe,增加退訂的方法

    function subscribe(listener) {listeners.push(listener);return function unsubscribe() {const index = listeners.indexOf(listener);listeners.splice(index, 1);
    };
    }

    使用:

    const unsubscribe = store.subscribe(() => {let state = store.getState();console.log(state.counter.count);
    });/*退訂*/unsubscribe();

    8|完善store

    到目前,applyMiddleware 方法中的 store 可以被中間件拿到,我們只把 getState 方法傳遞給中間件就好。 修改下 applyMiddleware 中給中間件傳的 store:

    /* * 以前是這樣的: * const chain = middlewares.map(middleware => middleware(store)); */const onlyGetStore = { getState: store.getState };const chain = middlewares.map(middleware => middleware(onlyGetStore));

    compose我們的 applyMiddleware 中,把 [A, B, C] 轉(zhuǎn)換成 A(B(C(next))),是這樣實現(xiàn)的

    const chain = [A, B, C];let dispatch = store.dispatch;chain.reverse().map(middleware => {
    dispatch = middleware(dispatch);
    });

    其實在 redux 中提供了一個 compose 方法,可以這樣做:

    const chain = [A, B, C];
    dispatch = compose(...chain)(store.dispatch);

    它的內(nèi)部實現(xiàn)如下:

    export default function compose(...funcs) {if (funcs.length === 1) {return funcs[0];
    }return funcs.reduce((a, b) => (...args) => a(b(...args)));
    }

    這個函數(shù)屬于函數(shù)式編程中的組合的概念,有興趣,我們后面單開一節(jié),講講函數(shù)式編程。

    9|省略initState

    在 redux 中有時候 createStore 的時候,沒有傳 initState,允許我們這么寫:

    const store = createStore(reducer, rewriteCreateStoreFunc);

    它的內(nèi)部做了這么一層實現(xiàn):

    function craeteStore(reducer, initState, rewriteCreateStoreFunc) {if (typeof initState === "function") {
    rewriteCreateStoreFunc = initState;
    initState = undefined;
    }/**其他代碼*/
    }

    10|按需加載reducer

    reducer 做了拆分之后,和 UI 組件是一一對應(yīng)的,在做按需加載的時候,reducer 也可以和組件一起做按需加載,用新的 reducer 替換老的 reducer:

    const createStore = function(reducer, initState) {function replaceReducer(nextReducer) {
    reducer = nextReducer;/*刷新一遍 state 的值,新來的 reducer 把自己的默認(rèn)狀態(tài)放到 state 樹上去*/dispatch({ type: Symbol() });
    }//其他代碼return {...replaceReducer
    };
    };

    使用的例子:

    const reducer = combineReducers({
    counter: counterReducer
    });const store = createStore(reducer);/*生成新的reducer*/const nextReducer = combineReducers({
    counter: counterReducer,
    info: infoReducer
    });/*replaceReducer*/store.replaceReducer(nextReducer);

    11|bindActionCreators

    bindActionCreators 一般只有在 react-redux 的 connect 實現(xiàn)中用到。

    他是做什么的?他通過閉包,把 dispatch 和 actionCreator 隱藏起來,讓其他地方感知不到 redux 的存在。

    我們通過普通的方式來 隱藏 dispatch 和 actionCreator 試試,注意最后兩行代碼

    const reducer = combineReducers({
    counter: counterReducer,
    info: infoReducer
    });const store = createStore(reducer);/*返回 action 的函數(shù)就叫 actionCreator*/function increment() {return {
    type: "INCREMENT"
    };
    }function setName(name) {return {
    type: "SET_NAME",
    name: name
    };
    }const actions = {increment: function() {return store.dispatch(increment.apply(this, arguments));
    },setName: function() {return store.dispatch(setName.apply(this, arguments));
    }
    };/*注意:我們可以把 actions 傳到任何地方去*//*其他地方在實現(xiàn)自增的時候,根本不知道 dispatch,actionCreator等細(xì)節(jié)*/actions.increment(); /*自增*/actions.setName("玄說前端"); /*修改 info.name*/

    actions 生成的時候,公共代碼,可以提取一下:

    const actions = bindActionCreators({ increment, setName }, store.dispatch);

    bindActionCreators 實現(xiàn):

    /*通過閉包隱藏了 actionCreator 和 dispatch,核心代碼*/function bindActionCreator(actionCreator, dispatch) {return function() {return dispatch(actionCreator.apply(this, arguments));
    };
    }/* actionCreators 必須是 function 或者 object */export default function bindActionCreators(actionCreators, dispatch) {if (typeof actionCreators === "function") {return bindActionCreator(actionCreators, dispatch);
    }if (typeof actionCreators !== "object" || actionCreators === null) {throw new Error();
    }const keys = Object.keys(actionCreators);const boundActionCreators = {};for (let i = 0; i < keys.length; i++) {const key = keys[i];const actionCreator = actionCreators[key];if (typeof actionCreator === "function") {
    boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
    }return boundActionCreators;
    }

    到目前為止基本的 redux 功能,都已實現(xiàn),由于版本迭代,不一定現(xiàn)有最新版本實現(xiàn)一致,但是最主要的思想和實現(xiàn)都是差不多的。

    源碼總結(jié)

    把關(guān)鍵的名詞提取出來:

    • createStore

      創(chuàng)建 store 對象,包含 getState, dispatch, subscribe, replaceReducer

    • reducer

      reducer 是一個計劃函數(shù),接收舊的 state 和 action,生成新的 state

    • action

      action 是一個對象,必須包含 type 字段

    • dispatch

    dispatch( action ) 觸發(fā) action,生成新的 state

    • subscribe

    實現(xiàn)訂閱功能,每次觸發(fā) dispatch 的時候,會執(zhí)行訂閱函數(shù)

    • combineReducers

    多 reducer 合并成一個 reducer

    • replaceReducer

    替換 reducer 函數(shù)

    • middleware

    擴展 dispatch 函數(shù)!

    這是基本的名詞都在這里!

    數(shù)據(jù)流程

    redux 的整體數(shù)據(jù)流如下圖:??更有味道的:?

    當(dāng)明白了 redux 之后,我們來看一下整體加上 view 之后的流程:?

    到目前為止,再去看看咱們上一篇文章中的一些問題,是不是又不一樣了, 下一篇文章咱們將會所有的問題全部詳解一遍,請保持關(guān)注

    注:文章部分內(nèi)容參考自brickspert

    END

    獲得更多信息

    關(guān)注公眾號

    總結(jié)

    以上是生活随笔為你收集整理的的源码管理器中有感叹图标_玄说前端面试层层解析—关于 redux 的源码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。