当前位置: 首页 > Web前端 > CSS

React生态,dva源码阅读

时间:2023-03-30 15:18:15 CSS

dva的思路还是很不错的,大大提高了开发效率。dva集成了Redux和Redux的中间件Redux-saga、React-router等,得益于Redux的状态管理,以及Redux-saga中通过Task和Effect处理异步的概念,dva在这些工具的基础上进行了高度封装,并且只公开了一些简单的API来设计数据模型。最近看了一下Redux-saga的源码,结合之前项目中一直使用的redux-dark模式,将reducers和sagas(generator函数异步处理)拆分成不同的子页面,同一个文件在每个页面都包含了页面状态的reducer和saga,这种简单的封装可以大大提高项目的可读性。最近看了dva的源码,熟悉了dva在上层是怎么封装的。下面将由浅入深,浅浅的了解一下自己在阅读dva源码过程中的理解。redux-darkmodedva0.0.12版本的使用和源码理解本文原文地址为:https://github.com/forthealll...特别是saga的reducer函数和generator函数如何存储,这两个功能直接关系到如何处理数据。回顾一下,在redux中使用异步中间件redux-saga后,完整的数据和信息流向:异步逻辑存在下,在UIComponent中发出一个普通的对象action,然后由中间件redux-saga处理,redux-saga会把这个action传入对应的channel,通过redux-saga的effect方法(比如call,put,apply等)生成一个描述对象,然后把这个描述对象转换成一个有副作用的函数执行它。当redux-saga执行带有副作用的函数时,它可以调度一个动作。这个action也是一个plainobject,会直接传入redux的reducer函数进行处理,也就是说在redux-saga的task中发送的action,是一个同步action。简单总结一下:UI组件发送的action经过了两层处理,分别是redux-saga中间件和redux的reducer函数。redux-dark模式非常简单。就是把redux-saga处理action的saga函数放在同一个子页面下,和reducer处理子页面下state的reducer函数放在同一个文件里。例如:import{connect}from'react-redux';classHelloextendsReact.Component{componentDidMount(){//发送action获取异步数据this.props.dispatch({type:'async_count'})}}exportdefaultconnect({})(Hello);从Hello组件发送一个type='async_count'的action,我们使用redux-dark模式将saga和reducer函数放在同一个文件中:import{takeEvery}from'redux-saga/effects';//sagafunction*asyncCount(){console.log('executedsagaasynchronous...')//发出原始操作yieldput({type:'async'});}function*helloSaga(){//接受来自UI组件的动作takeEvery('async_count',asyncCount);}//reducer函数helloReducer(state,action){if(action.type==='count');return{...state,count+1}}以上是将saga和reducer放在同一个文件中的例子。采用redux-dark模式组织代码,可以更直观,统一数据处理层。子页面拆分后,每个子页面对应一个文件。非常可读。2、dva0.0.12版本的使用及源码理解?上面redux-dark模式是一个简单的流程,同时对dva的话进行了进一步的封装。dva不仅封装了redux和redux-saga,还封装了react-router-redux、react-router等。它允许我们通过非常简单的配置来使用redux、redux-saga、react-router等。我们先以dva的初始版本为例,了解一下dva的源码。(1)、dva0.0.12的使用看官网给出的使用dva0.0.12的例子://1.Initializeconstapp=dva();//2.Modelapp.model({namespace:'count',state:0,effects:{['count/add']:function*(){console.log('count/add');yieldcall(delay,1000);yieldput({type:'count/minus',});},},reducers:{['count/add'](count){returncount+1},['count/minus'](count){returncount-1},},subscriptions:[function(dispatch){//..处理监控等功能}],});//3.ViewconstApp=connect(({count})=>({count}))(function(props){return(

{props.count}

{props.dispatch({type:'count/add'})}}>+{props.dispatch({type:'count/minus'})}}>-
);});//4。路由器应用程序。router(({history})=>);//5.startapp.start(document.getElementById('root'));就三步完成一个例子,action怎么处理,我们用一张图表示:即要做UI组件下发的对象类型的action,首先去根据type=去匹配model初始化的时候,如果effects属性中的action类型有对应的action类型处理函数在effects属性中,则先执行effects中对应的函数,在这个函数的执行中可以发出两次action,两次发出的action会直接传入reducer函数中,如果没有对应的action类型处理函数effects的属性,那么会直接从reducer中查找是否有对应类型的处理函数,dva初始化过程中effects属性中的函数其实就是redux-saga中的saga函数,其中直接异步逻辑被处理,函数可以发出第二个同步动作。另外dva还可以通过router方法初始化路由。(2)下面阅读dva0.0.12源码直接阅读dva0.0.12源码。以下代码是我精简后dva的源码://Providerglobalinjectionstoreimport{Provider}from'react-redux';//reduxrelatedapiimport{createStore,applyMiddleware,compose,combineReducers}from'redux';//redux-saga相关api,takeEvery和takeLatest监控等importcreateSagaMiddleware,{takeEvery,takeLatest}from'redux-saga';//react-router相关apiimport{hashHistory,Router}from'react-router';//react-router4.0之后已经很少用了,把路由的状态存储在store中reduceretc.import{handleActions}from'redux-actions';//redux-saga非阻塞调用effectimport{fork}from'redux-saga/effects';functiondva(){let_routes=null;const_models=[];//newdva公开了3个方法constapp={model,router,start,};返回应用程序;//添加模型,一个模型对象包含effects、reducers、subscriptionslisteners等。functionmodel(model){_models.push(model);}//添加路由函数router(routes){_routes=routes;}functionstart(container){letsagas={};//routing是react-router-redux的routerReducer别名,用于扩展reducer,方便扩展后的reducer以后处理路由Changeletreducers={routing};_models.forEach(model=>{//对每个模型,提取reducers和effects,其中reducers用于扩展redux的reducers功能,effects用于扩展redx-saga的saga处理功能。reducers[model.namespace]=handleActions(model.reducers||{},model.state);//扩展saga处理函数,sagas是一个包含所有saga处理函数的对象sagas={...sagas,...model.效果};--------------------------(1)});reducers={...reducers};//获取React-router中使用哪个api的决定const_history=opts.history||哈希历史;//初始化redux-sagaconstsagaMiddleware=createSagaMiddleware();//为redux添加中间件,这里添加处理路由中间件,redux-saga中间件。constenhancer=compose(applyMiddleware.apply(null,[routerMiddleware(_history),sagaMiddleware]),window.devToolsExtension?window.devToolsExtension():f=>f);constinitialState=opts.initialState||{};//通过combineReducers扩展reducer,同时生成一个扩展的store实例conststore=app.store=createStore(combineReducers(reducers),initialState,enhancer);//执行模型中的监听函数,将store.dispatch_models传入监听函数.forEach(({subscriptions})=>{if(subscriptions){subscriptions.forEach(sub=>{store.dispatch,onErrorWrapper);});}});//根据rootSaga启动saga,rootSaga是redux-saga运行的主要任务sagaMiddleware.run(rootSaga);//创建一个历史实例,可以监听store的状态变化。让历史;history=syncHistoryWithStore(_history,商店);------------------------------(2)//渲染和hmr。如果(容器){渲染();应用('onHmr')(渲染);}else{constRoutes=_routes;return()=>();}functiongetWatcher(k,saga){让_saga=saga;让_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'){returnsagaWithErrorCatch;}elseif(_type==='takeEvery'){returnfunction*(){yieldtakeEvery(k,sagaWithErrorCatch);};}else{返回函数*(){产量dtakeLatest(k,sagaWithErrorCatch);};}}function*rootSaga(){for(letkinsagas){if(sagas.hasOwnProperty(k)){constwatcher=getWatcher(k,sagas[k]);产量叉(观察者);}--------------------------(3)}}functionrender(routes){constRoutes=routes||_路线;ReactDOM.render((),container);}}}导出默认dva;上面阅读代码主要有3点值得注意:注释(一)中,handleActions是redux-actions封装的API,用于简化reducer函数的编写以下是handleActions的示例:constreducer=handleActions({INCREMENT:(state,action)=>({counter:state.counter+action.payload}),DECREMENT:(state,action)=>({counter:state.counter-action.payload})},{counter:0});INCREMENT和DECREMENT属性的功能可以分别处理type="INCREMENT"和type="DECREMENT"的动作。注(2)处,通过react-router-redux的API,syncHistoryWithStore可以扩展history,让history可以监听store的变化。注(3)是一个rootSaga,它是redux-saga运行时的mainTask。在这个任务中,我们这样定义它:产量叉(观察者);从包含所有saga函数的全局sagas对象中获取相应的属性,并fork出相应的monitor,这里有takeEvery和takeLatest两个常用的监控API。总结:以上是dva最早版本的源码,非常简洁地使用了redux、redux-saga、react-router、redux-actions、react-router-redux等。它的目的也很简单:简化redux相关生态的繁琐逻辑参考源码地址:https://github.com/dvajs/dva/...