的源码管理器中有感叹图标_玄说前端面试层层解析—关于 redux 的源码
“?前端面試精講系列,此篇文章是 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ā)布訂閱模式來解決這個問題。
代碼這樣處理:
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ù)器。
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。
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)一下:
};
如同上面的方法,調(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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode最小面积_Code Re
- 下一篇: flex将元素放在最后_前端布局——Fl