摘要:源碼個人覺得后的代碼就是,后返回的任然是一個函數,可以接受參數,在后面介紹的代碼中有所涉及。源碼中的獲取對象供后面使用,里的對應著對象里的,方法可以獲取,也就是對象樹的總數據。
redux介紹
redux給我們暴露了這幾個方法
{ createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
我們來依次介紹下
createStore創建一個store的寫法:
let store = createStore(reducer, preloadedState, enhancer);
createStore中的三個參數reducer, preloadedState, enhancer,后面兩個是可選參數,
當我們只傳兩個參數,并且第二個參數是函數時,例如:
let store = createStore(reducer, enhancer);
通過閱讀源碼這段
if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined }
我們知道,上面的createStore會被改寫成這樣:
createStore(educer, undefined, enhancer)
再通過閱讀源碼這段:
if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } return enhancer(createStore)(reducer, preloadedState) }
會發現原來的調用方式再次發生了改變,變成了以下這種方式
enhancer(createStore)(reducer, undefined)
所以實例化一個createStore方法,以下兩種是等價的:
第一種 const store = createStore(reducers, applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware)); 第二種 const store = applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware)(createStore)(reducers);
最后,返回的store對象提供了以下幾個方法給我們使用
dispatch, subscribe, getState, replaceReducer,
getState用來獲取currentState,也就是總state值
subscribe注冊多個監聽函數,這些函數在開發者調用dispatch時會依次執行
dispatch的入參是一個對象action,直接會執行reducer(action)方法,并且批量執行subscribe監聽的內容,dispatch執行結束后會返回一個action
replaceReducer替換當前的reducer,這個方法在異步的單應用中可以使用利用起來,例如,我們不想一次性將所有的reducer交給createStore初始化,而是當異步獲取某個頁面時,再將這個頁面的reducer加上之前的舊reducer,通過replaceReducer方法來替換成最新的。這樣做的好處是,devtools里的redux工具不會展示那么多的信息,只會展示訪問過頁面的信息,更有利于我們的開發,當然了由于reducer的減少,store的體積也會變小,頁面執行速度更快。
combineReducers源碼
export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== "production") { unexpectedKeyCache = {} } let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== "production") { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === "undefined") { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }
通常我們會這樣使用
combineReducers({ a:reducer1, b:reducer2 })
reducer1是一個函數,而combineReducers返回的任然是一個函數,只不過將每個reducer都遍歷了一遍,最后返回的數據結構為
{ a:state1, b:state2 }
而如果我們把a跟b替換成了一個唯一的路徑path,這個path跟項目中每個頁面的文件夾對應起來,例如:
combineReducers({ "component/page1/info/":reducer1, "component/page2/user/":reducer2 })
而在取state數據的時候這樣來取
state["component/page1/info/"]是不是就可以通過文件夾路徑實現模塊化了,當然了我們離模塊化還差一小步,就是不用人工來寫路徑path,而是交給構建工具(webpack的loader)來遍歷完成。
源碼
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
個人覺得compose(a,b,c)(d)后的代碼就是a(b(c(d))),compose后返回的任然是一個函數,可以接受參數,compose在后面介紹的代碼中有所涉及。
applyMiddleware源碼
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
創建一個store的方法:
let store = applyMiddleware(middleware1,middleware2,middleware3)(createStore)(reducers)
中間件middleware1的代碼
function middleware1({ getState }) { return (next) => (action) => { console.log("will dispatch", action) let returnValue = next(action) console.log("state after dispatch", getState()) return returnValue } }
applyMiddleware傳入的參數為多個中間件,中間件的作用是在執行reducers中的switch之前,先執行中間件中的代碼。
對應源碼中的參數來講的話,
實例參數middleware1、middleware2就相當于源碼入參...middlewares,
實例參數createStore對應著源碼入參createStore,
實例參數reducers對應著源碼入參...args。
源碼中的
const store = createStore(...args)
獲取store對象供后面使用,middlewareAPI里的getState對應著store對象里的getState,getState()方法可以獲取currentState,也就是redux對象樹的總數據。
chain = middlewares.map(middleware => middleware(middlewareAPI))
middlewareAPI提供了getState這個方法,供中間件獲取currenState,這時候chain獲取的數組是一個返回值為函數的函數。
下面這行代碼比較精彩
dispatch = compose(...chain)(store.dispatch)
首先將最原始的store.dispatch方法作為入參,
dispatch(action) 就相當于 compose(...chain)(store.dispatch)(action), 同時也相當于 middleware1(middleware2(middleware3((store.dispatch))))(action),
當然這里的middleware1是已經只剩下閉包內的兩層函數不是原來的三層函數體。
最先執行的是middleware1, 返回了next(action), 也就相當于 middleware2(middleware3((store.dispatch)))(action), 執行完后返回next(action) 就相當于middleware3((store.dispatch))(action), 執行完后返回next(action) 就相當于store.dispatch(action)
整個過程我們已經弄清楚了,applyMiddleware中間件的執行過程就是不斷的next(action),
而只有最后的next才是執行dispatch,之前的next只代表的傳遞其他中間件,dispatch方法只在最后一個中間件里執行了一次。
個人覺得這個地方結合了compose之后寫的比較精彩,而且設計的非常巧妙。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51699.html
摘要:源碼個人覺得后的代碼就是,后返回的任然是一個函數,可以接受參數,在后面介紹的代碼中有所涉及。源碼中的獲取對象供后面使用,里的對應著對象里的,方法可以獲取,也就是對象樹的總數據。 redux介紹 redux給我們暴露了這幾個方法 { createStore, combineReducers, bindActionCreators, applyMiddleware, c...
摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新??偠灾?,遵守這套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...
摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新??偠灾袷剡@套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...
摘要:到主菜了,先看它的一看,我們應該有個猜測,這貨是個高階函數。可能有點繞,但就是這么一個個高階函數組成的,后面會詳細說。定義了一個處理函數和高階函數執行次的方法,這個方法比上面的復雜在于它需要檢測參數是否訂閱了。 注意:文章很長,只想了解邏輯而不深入的,可以直接跳到總結部分。 初識 首先,從它暴露對外的API開始 ReactReduxContext /* 提供了 React.creat...
閱讀 2125·2019-08-29 16:53
閱讀 2700·2019-08-29 16:07
閱讀 2043·2019-08-29 13:13
閱讀 3268·2019-08-26 13:57
閱讀 1332·2019-08-26 13:31
閱讀 2434·2019-08-26 13:22
閱讀 1222·2019-08-26 11:43
閱讀 2085·2019-08-23 17:14