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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Redux源码全篇浅读

發(fā)布時(shí)間:2023/12/15 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redux源码全篇浅读 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文是關(guān)于 redux(3.7.2)源代碼的一些淺讀

在redux源碼目錄中 ,可以看到以下文件目錄:

|-- utils/|-- warning.js //打印error |-- 1. applyMiddleware.js // |-- 2. bindActionCreators.js // |-- 3. combineReducers.js // |-- 4. compose.js // |-- 5. createStore.js // |-- index.js //入口文件 復(fù)制代碼

與文件對應(yīng)的,主要也是介紹 createStore compose combineReducers bindActionCreators applyMiddleware這幾個(gè)函數(shù)。

1. compose

先來看看compose函數(shù),這個(gè)比較簡單。

export default function compose() {//funcs保存著所有參數(shù)函數(shù)的數(shù)組for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {funcs[_key] = arguments[_key];}//省略一些邏輯判斷……//reduce方法使用指定的函數(shù)將數(shù)組元素進(jìn)行組合,生成單個(gè)值return funcs.reduce(function (a, b) {return function () {return a(b.apply(undefined, arguments));};}); } 復(fù)制代碼

其中主要就是對 reduce 的理解了。reduce()方法使用指定的函數(shù)將數(shù)組元素進(jìn)行組合(從左到右),生成單個(gè)值。具體可以查看Array.prototype.reduce() | MDN

compose的功能為從右到左,組合參數(shù)(函數(shù))。傳遞給compose方法的參數(shù)是函數(shù)類型的,

compose(f, g, h) 相當(dāng)于 (...args) => f(g(h(...args)))

2. applyMiddleware 與 中間件鏈

故名思義這個(gè)函數(shù)就是應(yīng)用中間件,一般使用方式為:

//thunk代表react-thunk中間件 const store = createStore(rootReducer, initialState, applyMiddleware(thunk)); 復(fù)制代碼

2.1 中間件及react-thunk

在看applyMiddleware函數(shù)前,先來簡單看一下中間件,redux中間件在發(fā)起action到達(dá)reducer之間擴(kuò)展了一些功能。一般用于記錄日志(reudx-logger)和增加異步調(diào)用(redux-thunk , redux-saga)等。這些中間件一般按照一定的格式書寫,比如react-thunk2.2.0的源碼:

//react-thunk2.2.0的代碼,很簡單 只有十幾行…… //外層函數(shù)可以傳入多余的參數(shù) function createThunkMiddleware(extraArgument) {//獲取dispatch getState(是由applyMiddleware傳入)return function (_ref) {var dispatch = _ref.dispatch,getState = _ref.getState;//1. 如果這個(gè)中間件是調(diào)用鏈最內(nèi)環(huán)的,next指原store.dispatch //2. 其他next一般指上一個(gè)中間件的返回值 action => {}//對于這個(gè)next的賦值不清楚的話可以結(jié)合之后的applyMiddleware函數(shù)return function (next) {return function (action) {//1. 原action只是個(gè)普通的對象,thunk使action可以傳入函數(shù)類型,并傳入了dispatch, getState, extraArgument//2. 如果action是個(gè)異步函數(shù),thunk會調(diào)用該函數(shù)if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}//3. 函數(shù)調(diào)用結(jié)束后,獲取必要的數(shù)據(jù)再次觸發(fā)dispatch由此實(shí)現(xiàn)異步效果。return next(action);};};}; } 復(fù)制代碼

可以看到react-thunk中間件是一個(gè)多層的高階函數(shù),格式大致為:

({dispatch,getState}) => next => action => {return next(action)} 復(fù)制代碼

中間件的格式都要遵循這樣相似的格式,這是由applyMiddleware函數(shù)決定的。接下來看下applyMiddleware的源碼:

2.2 applyMiddleware源碼

//applyMiddleware源碼 export default function applyMiddleware() {//middlewares保存?zhèn)鬟M(jìn)來的中間件for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {middlewares[_key] = arguments[_key];}//createStore是創(chuàng)建createStore的函數(shù),會在下文解讀,這里先不管return function (createStore) {return function (reducer, preloadedState, enhancer) {//創(chuàng)建store 并獲取了其dispatch方法var store = createStore(reducer, preloadedState, enhancer);var _dispatch = store.dispatch;var chain = [];//用于傳遞給中間件第一層函數(shù)的參數(shù),上文在thunk中有看到var middlewareAPI = {getState: store.getState,dispatch: function dispatch(action) {return _dispatch(action);}};//middlewares保存的是中間件,chain對應(yīng)保存的就是中間件第二層函數(shù)組成的數(shù)組//形象點(diǎn)就是上文中間件格式去掉第一層:next => action => {}chain = middlewares.map(function (middleware) {return middleware(middlewareAPI);});//1. componse的功能在上文說到,假設(shè)chain為[F1,F2,F3],compose之后變成了F1(F2(F3))//2. 與上文thunk中說到中間件格式對應(yīng),F3是中間件鏈的最內(nèi)環(huán) 所以F3的next參數(shù)為store.dispatch//3. F2的next參數(shù)就是F3返回的 action => {}//4. 同樣的F1的next參數(shù)就是F2返回的 action => {} ////_dispatch就相當(dāng)于F1(F2(F3(store.dispatch)))//這樣多個(gè)中間件就組合到了一起,形成了中間件鏈_dispatch = compose.apply(undefined, chain)(store.dispatch);//新的dispatch會覆蓋原dispatch,之后調(diào)用dispatch同時(shí)會調(diào)用中間件鏈return _extends({}, store, {dispatch: _dispatch});};}; } 復(fù)制代碼

在_dispatch = compose.apply(undefined, chain)(store.dispatch);組合后,中間件的next就是store.dispatch一路經(jīng)過上一個(gè)中間件封裝后的變種dispatch。

2.3 中間件鏈的執(zhí)行順序

光看代碼可能對于中間件鏈的執(zhí)行順序的理解還是優(yōu)點(diǎn)蒙,這里來做個(gè)實(shí)踐加深一下理解。

一、 先按上文說的中間件格式({dispatch,getState}) => next => action => {return next(action)} 寫三個(gè)中間件:

function middleware1({dispatch,getState}) {return function(next) {console.log('middleware1 next層 next:',next);return function(action) {console.log('middleware1 action層 開始')next(action)console.log('middleware1 action層 結(jié)束')}} } function middleware2({dispatch,getState}) {return function(next) {console.log('middleware2 next層 next:',next);return function(action) {console.log('middleware2 action層 開始')next(action)console.log('middleware2 action層 結(jié)束')}} } function middleware3({dispatch,getState}) {return function(next) {console.log('middleware3 next層 next:',next);return function(action) {console.log('middleware3 action層 開始')next(action)console.log('middleware3 action層 結(jié)束')}} } 復(fù)制代碼

二、 將它們和redux-thunk 和 redux-logger一起加入中間件鏈:

const middlewares = [middleware1,thunkMiddleWare,middleware2,middleware3,loggerMiddleWare,//redux-logger需要放在最后面 ];...const store = createStore(rootReducer, initialState, applyMiddleware(...middlewares)); 復(fù)制代碼

運(yùn)行你的代碼后在chrome控制臺看到如下信息:

middleware3 next層 next: ? (l){if("function"==typeof …middleware2 next層 next: ? (action){ console.log('middleware3 action層 開始'); next(action); console.log('middleware3 action層 結(jié)束'); }middleware1 next層 next: ? (action) {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);} 復(fù)制代碼

這也驗(yàn)證了上文對 _dispatch = compose.apply(undefined, chain)(store.dispatch);的理解。

這里的中間件鏈?zhǔn)怯?[m1,thunk,m2,m3,logger]組成,在compose之后變成了 m1(thunk(m2(m3(logger)))), 所以

  • m3的next參數(shù)是logger的action層函數(shù)(參數(shù)為action那層函數(shù))
  • m2的next參數(shù)是m3的action層函數(shù)
  • m1的next參數(shù)是thunk的action層函數(shù)

三、 執(zhí)行一下 dispatch(action) :

這時(shí)候分兩種情況:

1、當(dāng)action類型為對象時(shí),在chrome控制臺看到如下信息:

發(fā)起 dispatch(action) action類型為對象middleware1 action層 開始 middleware2 action層 開始 middleware3 action層 開始 action SOME_OBJ_ACTION redux-logger.js:1 middleware3 action層 結(jié)束 middleware2 action層 結(jié)束 middleware1 action層 結(jié)束 復(fù)制代碼

粗粗一看好像順序不對啊,不該先執(zhí)行middleware3的邏輯嘛?其實(shí)內(nèi)層(位置靠后的中間件)只是返回了一個(gè)function,并沒有執(zhí)行其中的邏輯,不斷由外層的中間件包裹形成了一個(gè)‘洋蔥模型’。由外向內(nèi)穿心而過,再由內(nèi)向外完成流程。

這樣子就很明確了,中間件的action層執(zhí)行順序?yàn)橄燃尤胫虚g件鏈的先執(zhí)行!,更準(zhǔn)確的說中間件中先執(zhí)行從外層向內(nèi)層中 next(action)之前的邏輯,然后執(zhí)行從內(nèi)層向外層中 next(action)之后的邏輯。

2、當(dāng)action類型為函數(shù)時(shí),在chrome控制臺看到如下信息:

發(fā)起 dispatch(action) action類型為函數(shù)middleware1 action層 開始 middleware1 action層 結(jié)束 復(fù)制代碼

可以看到只執(zhí)行了 middleware1 和 thunk 兩個(gè)中間件!這是因?yàn)閠hunk沒有執(zhí)行 next(action) 中斷了中間件鏈! 當(dāng)中間件沒有執(zhí)行next(action)時(shí)會導(dǎo)致中間件鏈中斷,這是因?yàn)閐ispatch沒有傳遞下去,所以中間件還可以搗亂咯~

3. bindActionCreators

bindActionCreators的功能是為action creaters包裝上dispatch,使其調(diào)用action時(shí)自動dispatch對應(yīng)的action。

在bindActionCreators.js中只有兩個(gè)函數(shù) bindActionCreator 和 bindActionCreators。

//這個(gè)函數(shù)的主要作用就是返回一個(gè)函數(shù),當(dāng)我們調(diào)用返回的這個(gè)函數(shù)的時(shí)候,就會自動的dispatch對應(yīng)的action function bindActionCreator(actionCreator, dispatch) {return function () {return dispatch(actionCreator.apply(undefined, arguments));}; }export default function bindActionCreators(actionCreators, dispatch) {//省略一些邏輯判斷// 獲取所有action creater函數(shù)的名字var keys = Object.keys(actionCreators);// 保存dispatch和action creater函數(shù)進(jìn)行綁定之后的集合var boundActionCreators = {};//為每個(gè)actionCreators 包裝上 dispatchfor (var i = 0; i < keys.length; i++) {var key = keys[i];var actionCreator = actionCreators[key];if (typeof actionCreator === 'function') {boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);}}return boundActionCreators; } 復(fù)制代碼

4. combineReducers

Reducer只是一些純函數(shù),它接收之前的state和action,并返回新的state。當(dāng)應(yīng)用變大,我們可以拆分多個(gè)小的reducers,分別獨(dú)立的操作state tree的不同部分。而combineReducers就是將多個(gè)不同的reducer合并成一個(gè)最終的reducer,用于賦值給createStore函數(shù)。

//combineReducers的代碼比較簡單,在省略一些錯(cuò)誤判斷的代碼后: export default function combineReducers(reducers) { var reducerKeys = Object.keys(reducers);var finalReducers = {};for (var i = 0; i < reducerKeys.length; i++) {var key = reducerKeys[i];......if (typeof reducers[key] === 'function') {finalReducers[key] = reducers[key];}}var finalReducerKeys = Object.keys(finalReducers);//從上面到這里都是為了保存finalReducerKeys 和 finalReducers......//返回的combination函數(shù)就相當(dāng)于結(jié)合所有reducers之后新的reducerreturn function combination() {var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};var action = arguments[1];......var hasChanged = false;var nextState = {};//這里遍歷了所有之前自定義的reducers,并記錄下是否state有改變,并記錄下改變的statefor (var _i = 0; _i < finalReducerKeys.length; _i++) {var _key = finalReducerKeys[_i];var reducer = finalReducers[_key];var previousStateForKey = state[_key];//遍歷所有的reducer,若previousStateForKey匹配到則返回新的state//若匹配不到就在reducer中dufault中返回原statevar nextStateForKey = reducer(previousStateForKey, action);......nextState[_key] = nextStateForKey;//這里有點(diǎn)意思,并沒有因?yàn)檎业讲煌膕tate就直接返回//這意味著,多個(gè)子reducers可以對同個(gè)action返回自己的state//并且返回的state是依據(jù)靠后的reducer的返回值決定的hasChanged = hasChanged || nextStateForKey !== previousStateForKey;}return hasChanged ? nextState : state;}; } 復(fù)制代碼

5. createStore

現(xiàn)在來看下最重要的createStore函數(shù):

/* * redux有且僅有一個(gè)store,createStore函數(shù)就是用于創(chuàng)建一個(gè)store用來存放所有的state。 * @param {Function} reducer * @param {any} [preloadedState] 初始化state * @param {Function} [enhancer] store的增強(qiáng)器,有applyMiddleware、時(shí)間旅行(time travel)、持久化(persistence)等。 */ 復(fù)制代碼

上面是createStore的參數(shù),其中enhancer指的是store的增強(qiáng)器(對store API進(jìn)行改造)。

  • applyMiddleware在前面我們已經(jīng)見過了,applyMiddleware是對store的dispatch函數(shù)的修改。
  • 時(shí)間旅行則是指redux可以回到任意以前的狀態(tài)。
    • 這是因?yàn)镽edux使用簡單的對象來表示state狀態(tài),并使用純函數(shù)計(jì)算下一個(gè)狀態(tài)。這意味著如果給定一個(gè)特定state狀態(tài)和一個(gè)特定action操作,那么下一個(gè)狀態(tài)將始終完全相同。這種特性可以讓我們將所有修改過的狀態(tài)保存在一個(gè)狀態(tài)歷史對象中,通過指定恢復(fù)到狀態(tài)歷史對象中從前的狀態(tài),來完成時(shí)間旅行。
    • 比較容易可以想到的應(yīng)用就是一些撤銷/重做操作。
  • 持久化持久化這個(gè)概念肯定大家都熟悉,也有人做出了實(shí)現(xiàn):redux-persist

介紹完enhancer,來接著看代碼邏輯:

export default function createStore(reducer, preloadedState, enhancer) {var _ref2;if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {enhancer = preloadedState;preloadedState = undefined;}if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {throw new Error('Expected the enhancer to be a function.');}return enhancer(createStore)(reducer, preloadedState);}......var currentReducer = reducer; //var currentState = preloadedState; //當(dāng)前的statevar currentListeners = []; //訂閱函數(shù)var nextListeners = currentListeners;//訂閱函數(shù)備份,//用于解決listeners數(shù)組執(zhí)行過程(for循環(huán))中,取消訂閱listener產(chǎn)生的listeners數(shù)組index錯(cuò)誤。//這樣保證在某個(gè)dispatch后,會保證在這個(gè)dispatch之前的所有事件監(jiān)聽器全部執(zhí)行var isDispatching = false; //dispatch方法同步標(biāo)志function ensureCanMutateNextListeners() {if (nextListeners === currentListeners) {nextListeners = currentListeners.slice();}}/*** @returns {any} 當(dāng)前state*/function getState() {return currentState;}/** 將一個(gè)訂閱函數(shù)放到 listeners 隊(duì)列里,當(dāng) dispatch 的時(shí)候,逐一調(diào)用 listeners 中的回調(diào)方法。 * @param {Function} listener函數(shù)* @return {Function} 解除綁定的方法*/function subscribe(listener) {if (typeof listener !== 'function') {throw new Error('Expected listener to be a function.');}var isSubscribed = true;ensureCanMutateNextListeners();nextListeners.push(listener);//解除訂閱的方法,return function unsubscribe() {if (!isSubscribed) {return;}isSubscribed = false;ensureCanMutateNextListeners();var index = nextListeners.indexOf(listener);nextListeners.splice(index, 1);};}/*** dispatch方法,調(diào)用reducer** @param {Object} action ** @returns {Object} 一般會返回action,* 如果使用了中間件,可能返回promise 或者function之類的()*/function dispatch(action) {......//dispatch是同步的,用isDispatching標(biāo)志來判斷if (isDispatching) {throw new Error('Reducers may not dispatch actions.');}//調(diào)用reducertry {isDispatching = true;//更新state樹currentState = currentReducer(currentState, action);} finally {isDispatching = false;}//調(diào)用nextListeners中的監(jiān)聽方法var listeners = currentListeners = nextListeners;for (var i = 0; i < listeners.length; i++) {var listener = listeners[i];listener();}return action;}/*** 替換reducer*/function replaceReducer(nextReducer) {if (typeof nextReducer !== 'function') {throw new Error('Expected the nextReducer to be a function.');}currentReducer = nextReducer;//觸發(fā)生成新的state樹dispatch({ type: ActionTypes.INIT });}/***略*/function observable() {......}// 生成初始state樹dispatch({ type: ActionTypes.INIT });return _ref2 = {dispatch: dispatch,subscribe: subscribe,getState: getState,replaceReducer: replaceReducer}, _ref2[$$observable] = observable, _ref2; } 復(fù)制代碼

以上就是我個(gè)人的簡單見解,如果有什么錯(cuò)誤之處,敬請指導(dǎo)討論


參考資料:

  • Redux 入門教程(一):基本用法 | 阮一峰
  • Redux 中文文檔

總結(jié)

以上是生活随笔為你收集整理的Redux源码全篇浅读的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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