摘要:動態處理與,封裝了在運行時的進行一類增加和刪除的操作,例如可以再切換到某一路由時動態的加入一個個人猜測,熱更新很有可能也利用了這個兩個與。以上是本人對于的粗略的理解,內容如有錯誤,還請大家指出。
寫在前面
dva是螞蟻金服推出的一個單頁應用框架,對redux,react-router,redux-saga進行了上層封裝,沒有引入新的概念,但是極大的程度上提升了開發效率;下面內容為本人理解,如有錯誤,還請指出,不勝感激。
redux的痛苦redux的優點很多,痛點也有,比如異步控制,redux-saga的出現使得異步操作變得優雅,但是基于redux-saga不得不承認的一點就是開發過程實在是太麻煩了,假若增加一個操作,不得不操作actions,reducers,sagas,對于sagas可以還需要進行watch,而后還要進行fork;(PS: 本來就夠麻煩了,再加上一個sagas);在添加一個操作時,不得不操作這么多的文件,實在是麻煩,而dva的出現在一定程度上解決了這個問題。
dva基本概念未使用dva下的目錄經常是這樣的:
actions --/ user.js --/ team.js reducers --/ user.js --/ team.js sagas/ --/ user.js --/ team.js
dva將其合并:
models --/ user.js --/ team.js
dva中有著幾個概念:
namespace => combineReducers中對應的key值 state => 對應初始的state,也就是initialState effects => saga的處理函數 reducers => 對應reducers,不同的是,寫法上將switch...case轉化為對象
除了這些以外,dva中還有subscriptions,這一概念來源于elm,
dva的實現 初始化const app = dva({ history: browserHistory });
上面的過程發生了什么?
dva本質上調用了下面函數:
function dva(hooks = {}) { const history = hooks.history || defaultHistory; const initialState = hooks.initialState || {}; delete hooks.history; delete hooks.initialState; const plugin = new Plugin(); plugin.use(hooks); const app = { // properties _models: [], _router: null, _store: null, _history: null, _plugin: plugin, _getProvider: null, // methods use, model, router, start, }; return app; }
hooks為傳入的一些配置,例如可以通過傳入history來改變路由的實現,dva默認采用的是hashHistory;從這里可以看出dva暴露出來的方法:
app.router():指定路由,需要傳入一個函數,一般類似于({ history }) => (
app.use():添加插件,這個稍后來看~
app.model():添加model,也就是對應的添加一個store下的數據,該方法做的就是對傳入的model進行檢查,對reducers添加命名空間,而后將其push到_models中。
namespace必須且唯一,因為內置了react-redux-router,所以namespace也不能為routing
subscriptions與effects均為可選參數,傳入的話必須為對象
reducers為可選,支持對象和數組兩種傳入方式(傳入數組的方式,往往伴隨著高階reducer的應用,具體稍后再看~)
app.start():初始化應用,接受參數為選擇器或者DOM節點
需要注意的是:
reducers和effects的key不需要用namespace/action的形式了,因為dva會自動將其加上,dispatch的時候,saga需要加上namespace,而saga中的put不需要加入namespace,原因是dva對put進行了重載
dva同時支持rn應用,引入dva/mobile即可,這時react-router不在需要,利用rn中的Navigator即可,不會引用react-router與react-redux-router,namespace可以命名為routing;正是由于這點差異,作者將路由相關的內容作為參數傳入了進去,具體可以參見這個文件。
創建將一些配置項初始化好后,就可以app.start就是來創建一個應用,下面就一點點的看看start的過程(以下基于默認情況,也就是使用了react-router):
參數校驗,是否為DOM元素或者檢查是否可以根據傳入的選擇器字符串找到對應的DOM,這個DOM對應的就是ReactDOM.render的第二個參數。
錯誤處理,使得發生錯誤時,不至于應用奔潰,當然需要傳入自定義hooks.onError來處理:
// 傳入hooks.onError則調用,反之調用默認函數處理,拋出異常即可 const onError = plugin.apply("onError", (err) => { throw new Error(err.stack || err); }); // 目的是出現錯誤時,也可以進行dispatch操作 const onErrorWrapper = (err) => { if (err) { if (typeof err === "string") err = new Error(err); onError(err, app._store.dispatch); } };
遍歷_models,初始化reducers,sagas
const sagas = []; // initalReducer為{ routing: routerReducer } const reducers = { ...initialReducer }; // 為rootReducer for (const m of this._models) { // 得到默認的state reducers[m.namespace] = getReducer(m.reducers, m.state); if (m.effects) sagas.push(getSaga(m.effects, m, onErrorWrapper)); }處理reducers
對于redux的reducers最常見的是基于switch..case的,而dva做出了一些改變,將每一個case分支變作了一個函數:
(PS: 本人認為,這個可以塊可以更改,利用some操作來盡可能少的調用無意義的reducer,于是我提了一個pr)
每一個reducer的實現如下:
// actionType對應的是dva的reducers中的key值 (state, action) => { const { type } = action; if (type && actionType !== type) { return state; } return reducer(state, action); };處理sagas
看完了對于reducers的處理,下面來看一下對于sagas的處理:
function getSaga(effects, model, onError) { return function *() { for (const key in effects) { if (Object.prototype.hasOwnProperty.call(effects, key)) { const watcher = getWatcher(key, effects[key], model, onError); const task = yield sagaEffects.fork(watcher); // 為了移除時可以將saga任務注銷 yield sagaEffects.fork(function *() { yield sagaEffects.take(`${model.namespace}/@@CANCEL_EFFECTS`); yield sagaEffects.cancel(task); }); } } }; }
getWatcher返回一個saga監聽函數,也就是通常寫的watchXXX,model.effects[key]可以是一個任務函數;也可以是個數組,第一個參數為任務函數,第二為配置對象,可以傳入type,type有4個可選值,takeEvery(默認),takeLatest,throttle,watcher四種,dva對effects做了一個錯誤處理:
effect => function *(...args) { try { yield effect(...args.concat(createEffects(model))); } catch (e) { onError(e); // 為之前的onErrorWrapper } }
注意:
watcher是指傳入的任務函數就是一個watcher直接fork就好
throttle還要傳入一個ms配置,這個ms代表著在多少毫秒內只觸發一次同一類型saga任務,而takeEvery是不會限制同一類型執行次數,takeLatest只能執行一個同一類型任務,有執行中的再次執行就會取消
由getSaga可以看出,${namespace}/@@CANCEL_EFFECTS可以取消對應的任務監聽
可以通過配置hooks.onEffect來增加saga的watcher
增強reduxredux中間件,由sagaMiddware,routerMiddware(啟用react-router時),hooks.onAction傳入的其它中間件,如redux-logger等
其它增強,如redux-devtools,內置了redux-devtools,另需的話在hooks.extraReducers傳入
const enhancers = [ applyMiddleware(...middlewares), devtools(), ...extraEnhancers, ]; const store = this._store = createStore( // eslint-disable-line createReducer(), initialState, compose(...enhancers), );設置redux的回調函數
通過配置hooks.onStateChange可以指定redux的state改變后所觸發的回調函數:
const listeners = plugin.get("onStateChange"); for (const listener of listeners) { store.subscribe(() => { listener(store.getState()); }); } }新概念subscriptions
subscriptions是一個新概念,會在dom ready之后執行,在這里面可以做一些基礎數據的獲?。?br>一般會將初始數據的獲取放在react的生命周期中,比如componentWillMount,但是假設我們做了代碼分割,實現了按需加載,那么我們開始獲取數據的時間為:獲取相應的js+解析js+執行react生命周期,但是redux的數據加載和ui組件沒有太大關系,可以將數據獲取的時間點提前,subscriptions提供了解決方法,其意義為訂閱,對于上面的場景,我們可以訂閱路由,到了執行的路由執行相應的dispatch(),如:
setup({ dispatch, history }) { return history.listen(({ pathname, query }) => { if (pathname === "/users") { dispatch({ type: "fetch", payload: query }); } }); }
(PS: 對于這個新概念,我也不是很清楚,后面的文章會有專門的描述,大家先有一個概念就好)
掛載上述過程均為初始化的過程,就是獲取到需要的reducers,sagas以及對于一些中間件和插件的配置,下面要進行的就是掛載了,也就熟悉的render(
dva.model與dva.unmodel,封裝了在運行時的store進行一類增加和刪除的操作,例如可以再切換到某一路由時動態的加入一個model(個人猜測,熱更新很有可能也利用了這個兩個api與hooks.onHmr)。
未完結關于redux還有一個利器,那就是高階reduce,當然在dva中也有體現,這篇文章已經很長了,這些內容留在下一篇中介紹。以上是本人對于dva的粗略的理解,內容如有錯誤,還請大家指出。dva的確簡化了開發的流程,而且在螞蟻金服的很多業務線也有著應用,是一個很值得大家一試!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83474.html
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應一個文件??偨Y上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關生態的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態管理,以及Redux-saga中...
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應一個文件??偨Y上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關生態的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態管理,以及Redux-saga中...
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應一個文件??偨Y上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關生態的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態管理,以及Redux-saga中...
摘要:多端統一開發框架優秀學習資源匯總官方資源項目倉庫官方文檔項目倉庫官方文檔微信小程序官方文檔百度智能小程序官方文檔支付寶小程序官方文檔字節跳動小程序官方文檔文章教程不敢閱讀包源碼帶你揭秘背后的哲學從到構建適配不同端微信小程序等的應用小程序最 Awesome Taro 多端統一開發框架 Taro 優秀學習資源匯總 showImg(https://segmentfault.com/img/r...
閱讀 2892·2019-08-30 15:55
閱讀 1993·2019-08-30 14:02
閱讀 1231·2019-08-29 15:23
閱讀 1000·2019-08-29 11:27
閱讀 457·2019-08-26 11:43
閱讀 3184·2019-08-26 10:32
閱讀 1249·2019-08-23 14:41
閱讀 3295·2019-08-23 14:41