摘要:下面會(huì)從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個(gè)子頁面對(duì)應(yīng)一個(gè)文件。總結(jié)上面就是最早版本的源碼,很簡(jiǎn)潔的使用了等其目的也很簡(jiǎn)單簡(jiǎn)化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址
??dva的思想還是很不錯(cuò)的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中通過Task和Effect來處理異步的概念,dva在這些工具的基礎(chǔ)上高度封裝,只暴露出幾個(gè)簡(jiǎn)單的API就可以設(shè)計(jì)數(shù)據(jù)模型。
??最近看了一下Redux-saga的源碼,結(jié)合以及之前在項(xiàng)目中一直采用的是redux-dark模式來將reducers和sagas(generator函數(shù)處理異步)拆分到不同的子頁面,每一個(gè)頁面中同一個(gè)文件中包含了該頁面狀態(tài)的reducer和saga,這種簡(jiǎn)單的封裝已經(jīng)可以大大的提升項(xiàng)目的可讀性。
??最近看了dva源碼,熟悉了dva是在上層如何做封裝的。下面會(huì)從淺到深,淡淡在閱讀dva源碼過程中自己的理解。
redux-dark模式
dva 0.0.12版本的使用和源碼理解
本文的原文地址為: https://github.com/forthealll...
歡迎star
??在使用redux和redux-saga的時(shí)候,特別是如何存放reducer函數(shù)和saga的generator函數(shù),這兩個(gè)函數(shù)是直接跟如何處理數(shù)據(jù)掛鉤的。
??回顧一下,在redux中使用異步中間件redux-saga后,完整的數(shù)據(jù)和信息流向:
??在存在異步的邏輯下,在UI Component中發(fā)出一個(gè)plain object的action,然后經(jīng)過redux-saga這個(gè)中間件處理,redux-saga會(huì)將這個(gè)action傳入相應(yīng)channel,通過redux-saga的effect方法(比如call、put、apply等方法)生成一個(gè)描述對(duì)象,然后將這個(gè)描述對(duì)象轉(zhuǎn)化成具有副作用的函數(shù)并執(zhí)行。
??在redux-saga執(zhí)行具有副作用的函數(shù)時(shí),又可以dispatch一個(gè)action,這個(gè)action也是一個(gè)plain object,會(huì)直接傳入到redux的reducer函數(shù)中進(jìn)行處理,也就是說在redux-saga的task中發(fā)出的action,就是同步的action。
簡(jiǎn)單的概括:從UI組件上發(fā)出的action經(jīng)過了2層的處理,分別是redux-saga中間件和redux的reducer函數(shù)。
??redux-dark模式很簡(jiǎn)單,就是將同一個(gè)子頁面下的redux-saga處理action的saga函數(shù),以及reducer處理該子頁面下的state的reducer函數(shù),放在同一個(gè)文件中。
舉例來說:
import { connect } from "react-redux"; class Hello extends React.Component{ componentDidMount(){ //發(fā)出一個(gè)action,獲取異步數(shù)據(jù) this.props.dispatch({ type:"async_count" }) } } export default connect({})(Hello);
從Hello組件中發(fā)出一個(gè)type = "async_count"的action,我們用redux-dark模式來將saga和reducer函數(shù)放在同一個(gè)文件中:
import { takeEvery } from "redux-saga/effects"; //saga function * asyncCount(){ console.log("執(zhí)行了saga異步...") //發(fā)出一個(gè)原始的action yield put({ type:"async" }); } function * helloSaga(){ //接受從UI組件發(fā)出的action takeEvery("async_count",asyncCount); } //reducer function helloReducer(state,action){ if(action.type === "count"); return { ...state,count + 1} }
上述就是一個(gè)將saga和reducer放在同一個(gè)文件里面的例子。redux-dark模式來組織代碼,可以顯得比較直觀,統(tǒng)一了數(shù)據(jù)的處理層。分拆子頁面后,每一個(gè)子頁面對(duì)應(yīng)一個(gè)文件。可讀性很高。
二、dva 0.0.12版本的使用和源碼理解??上述的redux-dark模式,就是一種簡(jiǎn)單的處理,而dva的話是做了更近一步的封裝,dva不僅封裝了redux和redux-saga,還有react-router-redux、react-router等等。使得我們可以通過很簡(jiǎn)單的配置,就能使用redux、redux-saga、react-router等。
下面首先以dva的初始版本為例來理解一下dva的源碼。
(1)、dva 0.0.12的使用來看官網(wǎng)給的使用dva 0.0.12的例子:
// 1. Initialize const app = dva(); // 2. Model app.model({ namespace: "count", state: 0, effects: { ["count/add"]: function*() { console.log("count/add"); yield call(delay, 1000); yield put({ type: "count/minus", }); }, }, reducers: { ["count/add" ](count) { return count + 1 }, ["count/minus"](count) { return count - 1 }, }, subscriptions: [ function(dispatch) { //..處理監(jiān)聽等等函數(shù) } ], }); // 3. View const App = connect(({ count }) => ({ count }))(function(props) { return (); }); // 4. Router app.router(({ history }) =>{ props.count }
); // 5. Start app.start(document.getElementById("root"));
只要三步就完成了一個(gè)例子,如何處理action呢,我們以一個(gè)圖來表示:
也就是做UI組件上發(fā)出的對(duì)象類型的action,先去根據(jù)類型匹配=model初始化時(shí)候,effects屬性中的action type。
如果在effects的屬性中有相應(yīng)的action type的處理函數(shù),那么先執(zhí)行effects中的相應(yīng)函數(shù),在執(zhí)行這個(gè)函數(shù)里面可以二次發(fā)出action,二次發(fā)出的action會(huì)直接傳入到reducer函數(shù)中。
如果effects的屬性中沒有相應(yīng)的action type的處理函數(shù),那么會(huì)直接從reducer中尋找有沒有相應(yīng)類型的處理函數(shù)。
在dva初始化過程中的effects屬性中的函數(shù),其實(shí)就是redux-saga中的saga函數(shù),在該函數(shù)中處理直接的異步邏輯,并且該函數(shù)可以二次發(fā)出同步的action。
此外dva還可以通過router方法初始化路由等。
(2)、dva 0.0.12的源碼閱讀下面來直接讀讀dva 0.0.12的源碼,下面的代碼是經(jīng)過我精簡(jiǎn)后的dva的源碼:
//Provider全局注入store import { Provider } from "react-redux"; //redux相關(guān)的api import { createStore, applyMiddleware, compose, combineReducers } from "redux"; //redux-saga相關(guān)的api,takeEvery和takeLatest監(jiān)聽等等 import createSagaMiddleware, { takeEvery, takeLatest } from "redux-saga"; //react-router相關(guān)的api import { hashHistory, Router } from "react-router"; //在react-router4.0之后已經(jīng)較少使用,將路由的狀態(tài)存儲(chǔ)在store中 import { routerMiddleware, syncHistoryWithStore, routerReducer as routing } from "react-router-redux"; //redux-actions的api,可以以函數(shù)式描述reducer等 import { handleActions } from "redux-actions"; //redux-saga非阻塞調(diào)用effect import { fork } from "redux-saga/effects"; function dva() { let _routes = null; const _models = []; //new dva暴露了3個(gè)方法 const app = { model, router, start, }; return app; //添加models,一個(gè)model對(duì)象包含了effects,reducers,subscriptions監(jiān)聽器等等 function model(model) { _models.push(model); } //添加路由 function router(routes) { _routes = routes; } function start(container) { let sagas = {}; //routing是react-router-redux的routerReducer別名,用于擴(kuò)展reducer,這樣以后擴(kuò)展后的reducer就可以處理路由變化。 let reducers = { routing }; _models.forEach(model => { //對(duì)于每一個(gè)model,提取其中的reducers和effects,其中reducers用于擴(kuò)展redux的reducers函數(shù),而effects用于擴(kuò)展redx-saga的saga處理函數(shù)。 reducers[model.namespace] = handleActions(model.reducers || {}, model.state); //擴(kuò)展saga處理函數(shù),sagas是包含了所有的saga處理函數(shù)的對(duì)象 sagas = { ...sagas, ...model.effects }; ---------------------------(1) }); reducers = { ...reducers }; //獲取決定使用React-router中的那一個(gè)api const _history = opts.history || hashHistory; //初始化redux-saga const sagaMiddleware = createSagaMiddleware(); //為redux添加中間件,這里添加了處理路由的中間件,以及redux-saga中間件。 const enhancer = compose( applyMiddleware.apply(null, [ routerMiddleware(_history), sagaMiddleware ]), window.devToolsExtension ? window.devToolsExtension() : f => f ); const initialState = opts.initialState || {}; //通過combineReducers來擴(kuò)展reducers,同時(shí)生成擴(kuò)展后的store實(shí)例 const store = app.store = createStore( combineReducers(reducers), initialState, enhancer ); // 執(zhí)行model中的監(jiān)聽函數(shù),監(jiān)聽函數(shù)中傳入store.dispatch _models.forEach(({ subscriptions }) => { if (subscriptions) { subscriptions.forEach(sub => { store.dispatch, onErrorWrapper); }); } }); // 根據(jù)rootSaga來啟動(dòng)saga,rootSaga就是redux-saga運(yùn)行的主task sagaMiddleware.run(rootSaga); //創(chuàng)建history實(shí)例子,可以監(jiān)聽store中的state的變化。 let history; history = syncHistoryWithStore(_history, store); --------------------------------(2) // Render and hmr. if (container) { render(); apply("onHmr")(render); } else { const Routes = _routes; return () => (); } function getWatcher(k, saga) { let _saga = saga; let _type = "takeEvery"; if (Array.isArray(saga)) { [ _saga, opts ] = saga; _type = opts.type; } function* sagaWithErrorCatch(...arg) { try { yield _saga(...arg); } catch (e) { onError(e); } } if (_type === "watcher") { return sagaWithErrorCatch; } else if (_type === "takeEvery") { return function*() { yield takeEvery(k, sagaWithErrorCatch); }; } else { return function*() { yield takeLatest(k, sagaWithErrorCatch); }; } } function* rootSaga() { for (let k in sagas) { if (sagas.hasOwnProperty(k)) { const watcher = getWatcher(k, sagas[k]); yield fork(watcher); } -----------------------------(3) } } function render(routes) { const Routes = routes || _routes; ReactDOM.render(( ), container); } } } export default dva;
代碼的閱讀在上面都以注視的方式給出,值得注意的主要有一下3點(diǎn):
在注釋(1)處, handleActions是通過redux-actions封裝后的一個(gè)API,用于簡(jiǎn)化reducer函數(shù)的書寫。下面是一個(gè)handleActions的例子:
const reducer = handleActions( { INCREMENT: (state, action) => ({ counter: state.counter + action.payload }), ? DECREMENT: (state, action) => ({ counter: state.counter - action.payload }) }, { counter: 0 } );
INCREMENT和DECREMENT屬性的函數(shù)就可以分別處理,type = "INCREMENT"和type = "DECREMENT"的action。
在注釋 (2) 處,通過react-router-redux的api,syncHistoryWithStore可以擴(kuò)展history,使得history可以監(jiān)聽到store的變化。
在注釋(3)處是一個(gè)rootSaga, 是redux-saga運(yùn)行的時(shí)候的主Task,在這個(gè)Task中我們這樣定義:
function* rootSaga() { for (let k in sagas) { if (sagas.hasOwnProperty(k)) { const watcher = getWatcher(k, sagas[k]); yield fork(watcher); } } }
從全局的包含所有saga函數(shù)的sagas對(duì)象中,獲取相應(yīng)的屬性,并fork相應(yīng)的監(jiān)聽,這里的監(jiān)聽常用的有takeEvery和takeLatest等兩個(gè)redux-saga的API等。
總結(jié):上面就是dva最早版本的源碼,很簡(jiǎn)潔的使用了redux、redux-saga、react-router、redux-actions、react-router-redux等.其目的也很簡(jiǎn)單:
簡(jiǎn)化redux相關(guān)生態(tài)的繁瑣邏輯
參考源碼地址:https://github.com/dvajs/dva/...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/53616.html
摘要:下面會(huì)從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個(gè)子頁面對(duì)應(yīng)一個(gè)文件。總結(jié)上面就是最早版本的源碼,很簡(jiǎn)潔的使用了等其目的也很簡(jiǎn)單簡(jiǎn)化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯(cuò)的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
摘要:下面會(huì)從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個(gè)子頁面對(duì)應(yīng)一個(gè)文件。總結(jié)上面就是最早版本的源碼,很簡(jiǎn)潔的使用了等其目的也很簡(jiǎn)單簡(jiǎn)化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯(cuò)的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
摘要:調(diào)用通過注冊(cè)表調(diào)用到實(shí)例,透過的,調(diào)用到中的,最后通過,調(diào)用,根據(jù)參數(shù)相應(yīng)模塊執(zhí)行。京東的,多端解決方案是一套遵循語法規(guī)范的多端開發(fā)解決方案。 showImg(https://segmentfault.com/img/bVbuMkw?w=1304&h=808); 對(duì)于一項(xiàng)技術(shù),我們不能停留在五分鐘狀態(tài),特別喜歡一句話,用什么方式繪制UI界面一點(diǎn)不重要,重要的是底層的思維,解決問題和優(yōu)化...
摘要:調(diào)用通過注冊(cè)表調(diào)用到實(shí)例,透過的,調(diào)用到中的,最后通過,調(diào)用,根據(jù)參數(shù)相應(yīng)模塊執(zhí)行。京東的,多端解決方案是一套遵循語法規(guī)范的多端開發(fā)解決方案。 showImg(https://segmentfault.com/img/bVbuMkw?w=1304&h=808); 對(duì)于一項(xiàng)技術(shù),我們不能停留在五分鐘狀態(tài),特別喜歡一句話,用什么方式繪制UI界面一點(diǎn)不重要,重要的是底層的思維,解決問題和優(yōu)化...
閱讀 1552·2023-04-26 01:36
閱讀 2717·2021-10-08 10:05
閱讀 2774·2021-08-05 09:57
閱讀 1536·2019-08-30 15:52
閱讀 1192·2019-08-30 14:12
閱讀 1310·2019-08-30 11:17
閱讀 3096·2019-08-29 13:07
閱讀 2415·2019-08-29 12:35