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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

dva源码解析(一)

發布時間:2025/3/15 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dva源码解析(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載 :原文 https://blog.csdn.net/zp1996323/article/details/73315096

寫在前面

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 }) => (<Router>...</Router>)
- 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;正是由于這點差異,作者將路由相關的內容作為參數傳入了進去,具體可以參見[這個文件][1]。

創建

將一些配置項初始化好后,就可以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

增強redux

  • redux中間件,由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-linecreateReducer(),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之后執行,在這里面可以做一些基礎數據的獲取:
一般會將初始數據的獲取放在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(<Provider>, container)。

動態處理model

dva.model與dva.unmodel,封裝了在運行時的store進行一類增加和刪除的操作,例如可以再切換到某一路由時動態的加入一個model(個人猜測,熱更新很有可能也利用了這個兩個api與hooks.onHmr)。

未完結

關于redux還有一個利器,那就是高階reduce,當然在dva中也有體現,這篇文章已經很長了,這些內容留在下一篇中介紹。以上是本人對于dva的粗略的理解,內容如有錯誤,還請大家指出。dva的確簡化了開發的流程,而且在螞蟻金服的很多業務線也有著應用,是一個很值得大家一試!

轉載于:https://www.cnblogs.com/Chasel-Chen/p/9550188.html

新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!

總結

以上是生活随笔為你收集整理的dva源码解析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。