摘要:核心源碼解析本文默認(rèn),你已經(jīng)有了一定的基礎(chǔ)。定義如何更新初始狀態(tài)是讓中間件按照規(guī)定模型洋蔥模型回形針模型執(zhí)行的函數(shù),就是下文將會(huì)介紹的函數(shù)的返回值返回中的。洋蔥模型回形針模型調(diào)用后的結(jié)果對(duì)上面的代碼換一種展現(xiàn)方式中間件要執(zhí)行,先得執(zhí)行。
redux 核心源碼解析
本文默認(rèn),你已經(jīng)有了一定的es6基礎(chǔ)。源碼有刪減,對(duì)其中的核心邏輯做解釋
redux 是 一個(gè)用 javascript 管理數(shù)據(jù)的容器,這個(gè)容器的狀態(tài)是可以預(yù)測(cè)的。
redux 可以跟任何是個(gè) 視圖(view)層的框架結(jié)合使用
開發(fā)react應(yīng)用,經(jīng)常結(jié)合使用 redux
redux 比較抽象,核心旨在定義一個(gè)數(shù)據(jù)讀寫的規(guī)范,和數(shù)據(jù)變化后的回調(diào)接口
三大原則單一數(shù)據(jù)源 :整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中,并且這個(gè) object tree 只存在于唯一一個(gè) store 中。
State 是只讀的:惟一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象。
使用純函數(shù)來(lái)執(zhí)行修改 :為了描述 action 如何改變 state tree ,你需要編寫 reducers。純函數(shù):http://web.jobbole.com/86136/
createStore.js 核心邏輯function createStore(reducer, preloadedState, enhancer) { /* 參數(shù)格式/類型 匹配。 reducer:定義 state 如何更新 preloadedState: 初始狀態(tài) enhancer 是讓中間件按照規(guī)定模型(洋蔥模型/回形針模型)執(zhí)行的函數(shù),就是下文將會(huì)介紹的 applyMiddleware函數(shù)的返回值 */ 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) } let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners /** * 返回 store 中的 state。 * State 是只讀的:這個(gè) state 是 createStore 函數(shù)內(nèi)部變量,只能通過(guò) createStore 提供的 getState 方法讀取 state。 */ function getState() { return currentState } /** * listener 是函數(shù),維護(hù)在內(nèi)部變量 nextListeners(數(shù)組) 中 * 調(diào)用 subscribe(訂閱) 方法,添加 listener 在數(shù)組中 * 并返回一個(gè)方法,可以把當(dāng)前添加在數(shù)組中的 listener 函數(shù)取出。 * * listener 的作用:當(dāng) state 發(fā)生變化的時(shí)候,listener具體去做些事情,比如更新 UI。 * 只定義做事情的時(shí)機(jī),具體做什么由調(diào)用者自己實(shí)現(xiàn) */ function subscribe(listener) { nextListeners.push(listener) return function unsubscribe() { const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } /** * dispatch的作用:更改 state * 更改的方式:根據(jù)傳入的 reducer 和 action,產(chǎn)生一個(gè)新的 state。 * * action的要求:是一個(gè)純粹的對(duì)象字面量,并且具有 type 字段 * 經(jīng)過(guò) reducer 根據(jù) action 把 當(dāng)前 state 更新。一旦更新結(jié)束,立即把數(shù)組中的每一個(gè) listener 調(diào)用一遍 */ function dispatch(action) { if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } currentState = currentReducer(currentState, action) const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } /** * 可以更改 reducer * reducer 一旦更改,立即 dispatch 一次,把 state 更新,把注冊(cè)的 listener 執(zhí)行一遍 */ function replaceReducer(nextReducer) { if (typeof nextReducer !== "function") { throw new Error("Expected the nextReducer to be a function.") } currentReducer = nextReducer dispatch({ type: ActionTypes.REPLACE }) } /** * 一旦 調(diào)用 createStore 函數(shù),立即 dispatch 一遍,把 state 更新,把注冊(cè)的 listener 執(zhí)行一遍 */ dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, } }redux 中的中間件
redux 中的中間件一個(gè)典型的應(yīng)用場(chǎng)景是處理異步接口返回?cái)?shù)據(jù)
常見(jiàn)的 web 系統(tǒng)中的中間件,不侵入業(yè)務(wù)邏輯,功能優(yōu)盤化,即用即插。
middleWareA(middleWareB(middleWareC("hello world"))); /** * 調(diào)用后的結(jié)果: * hello world form middleWareC;form middleWareB;form middleWareA; * */ function middleWareA(string){ return `${string} form middleWareA;`; }; function middleWareB(string){ return `${string} form middleWareB;`; }; function middleWareC(string){ return `${string} form middleWareC;`; };
對(duì)上面的代碼換一種展現(xiàn)方式
middleWareA( middleWareB( middleWareC("hello world") ) );
中間件 middleWareA 要執(zhí)行,先得執(zhí)行 middleWareB。中間件 middleWareB 要執(zhí)行,先得執(zhí)行 middleWareC,數(shù)據(jù)層層傳遞
調(diào)用順序?yàn)椋?A-->B-->C
執(zhí)行結(jié)束的順序?yàn)椋篊-->B-->A
這就是 洋蔥模型 或者 回形針模型
下面看一個(gè) redux-thunk 中間件
function createThunkMiddleware() { /** * dispatch 是基于 store.dispatch,且層層經(jīng)過(guò)中間件包裝后的方法 * getState 是 createStore 函數(shù)執(zhí)行后導(dǎo)出的方法; * next 為下一個(gè)中間件 * action 的預(yù)期格式為 createStore 中設(shè)計(jì)的 action的格式,是一個(gè)字面量對(duì)象。在這里做action類型的判斷 */ return ({ dispatch, getState }) => next => action => { // 如果 action 是函數(shù) if (typeof action === "function") { return action(dispatch, getState); } // 如果不是函數(shù),流轉(zhuǎn)到下一個(gè)中間件,期待最后的一個(gè)中間件的 action 是一個(gè)預(yù)期模式的 action return next(action); }; } export default createThunkMiddleware();applyMiddleware 核心邏輯
/** * middlewares 是包含中間件的數(shù)組 * 需要的中間件,依次傳入。如: applyMiddleware(middlewaresA, middlewaresB, middlewaresC) * applyMiddleware 執(zhí)行后的返回的函數(shù)就是一個(gè) enhancer */ export default function applyMiddleware(...middlewares) { return createStore => (...args) => { // args解構(gòu)后,就是 reducer, preloadedState // 這里的 store 就是 createStore 函數(shù)生產(chǎn)的全局的狀態(tài)樹,參見(jiàn)上文對(duì) createStore 的解釋 const store = createStore(...args) let dispatch = () => { }; let chain = [] /** * 每個(gè)中間件中需要傳遞的數(shù)據(jù) */ const middlewareAPI = { getState: store.getState,// 獲取 state 的函數(shù),用于讀 dispatch: (...args) => dispatch(...args)// 更新 state 的函數(shù),用于寫。 } // 遍歷中間件 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 下面的代碼的效果,就是要運(yùn)用洋蔥模型/回形針模型:middlewareA(middlewareB(middlewareC(store.dispatch))) // 關(guān)于 compose 比較簡(jiǎn)單:https://github.com/reactjs/redux/blob/master/src/compose.js // 中間件的開始是 原生的store.dispatch,結(jié)束時(shí)是 一個(gè)攜帶了各種中間件信息的 dispatch dispatch = compose(...chain)(store.dispatch) /** * 尤其注意這里的 dispatch * 開始時(shí)定義了一個(gè)空的 dispatch 函數(shù),是為了給 middlewareAPI 的 dispatch 一個(gè)初始值 * 實(shí)際上真正的 dispatch 是中間件根據(jù)原生的 store.dispatch 按照洋蔥模型/回形針模型,執(zhí)行了一邊,調(diào)用所有中間件后的 dispatch * 這個(gè) dispatch 攜帶了傳入的每一個(gè)中間件的操作。一次如果此時(shí)傳入 action 更新 state,所有的中間件都會(huì)被執(zhí)行一遍。 */ return { ...store, dispatch } } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/92645.html
摘要:此篇文章可作為源碼導(dǎo)讀使用,只說(shuō)明了其中部分核心代碼,并進(jìn)行了一些簡(jiǎn)化處理用法回顧用來(lái)創(chuàng)建創(chuàng)建在中編寫純函數(shù)來(lái)處理用通過(guò)和來(lái)操作里的數(shù)據(jù)用來(lái)監(jiān)聽中的數(shù)據(jù)是否發(fā)生了變化用來(lái)獲取中的,并更新視圖核心代碼初始化數(shù)據(jù)存放,是傳入的默認(rèn)值存放傳入的創(chuàng) 此篇文章可作為redux源碼導(dǎo)讀使用,只說(shuō)明了其中部分核心代碼,并進(jìn)行了一些簡(jiǎn)化處理 用法回顧 用createStore來(lái)創(chuàng)建store 創(chuàng)建A...
摘要:源碼解析模塊的代碼十分簡(jiǎn)練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。只傳遞一個(gè)參數(shù)的時(shí)候,就直接把這個(gè)函數(shù)返回返回組合函數(shù)這就是對(duì)源碼的一個(gè)整體解讀,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測(cè)試?yán)涌梢躁P(guān)注源碼解讀倉(cāng)庫(kù) compose源碼解析 compose模塊的代碼十分簡(jiǎn)練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。redux為何稱為redux?有人說(shuō)就是reduce和flux的結(jié)合體,而reduce正是comp...
摘要:源碼解析是最核心的模塊。比如,當(dāng)我們需要使用中間件的時(shí)候,就會(huì)像第三個(gè)參數(shù)傳遞一個(gè)返回值是一個(gè)。后續(xù)的源碼解讀和測(cè)試?yán)涌梢躁P(guān)注源碼解讀倉(cāng)庫(kù) createStore源碼解析 createStore是redux最核心的模塊。這個(gè)模塊就是用于創(chuàng)建一個(gè)store對(duì)象,同時(shí),對(duì)外暴露出dispatch,getState,subscribe和replaceReducer方法。(源碼中關(guān)于obse...
摘要:然后循環(huán)調(diào)用中的更新函數(shù),更新函數(shù)一般是我們的渲染函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用來(lái)獲取數(shù)據(jù),所以頁(yè)面會(huì)更新。 歡迎訪問(wèn)個(gè)人網(wǎng)站:https://www.neroht.com/ 前言 前幾天寫了一篇react另一個(gè)狀態(tài)管理工具Unstated的源碼解析。開啟了我的看源碼之路。想一想用了好長(zhǎng)時(shí)間的redux,但從沒(méi)有深究過(guò)原理,遇到報(bào)錯(cuò)更是懵逼,所以就啃了一遍它的源碼,寫了這篇文章,分享我對(duì)于它的理...
摘要:然后循環(huán)調(diào)用中的更新函數(shù),更新函數(shù)一般是我們的渲染函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用來(lái)獲取數(shù)據(jù),所以頁(yè)面會(huì)更新。前言 前幾天寫了一篇react另一個(gè)狀態(tài)管理工具Unstated的源碼解析。 開啟了我的看源碼之路。想一想用了好長(zhǎng)時(shí)間的redux,但從沒(méi)有深究過(guò)原理,遇到報(bào)錯(cuò)更是懵逼,所以就啃了一遍它的源碼,寫了這篇文章, 分享我對(duì)于它的理解。 API概覽 看一下redux源碼的index.js,看到了我們最...
閱讀 3040·2021-11-23 09:51
閱讀 1725·2021-10-15 09:39
閱讀 1125·2021-08-03 14:03
閱讀 2962·2019-08-30 15:53
閱讀 3488·2019-08-30 15:52
閱讀 2565·2019-08-29 16:17
閱讀 2885·2019-08-29 16:12
閱讀 1708·2019-08-29 15:26