We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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-dark模式 dva 0.0.12版本的使用和源码理解
在使用redux和redux-saga的时候,特别是如何存放reducer函数和saga的generator函数,这两个函数是直接跟如何处理数据挂钩的。
回顾一下,在redux中使用异步中间件redux-saga后,完整的数据和信息流向:
在存在异步的逻辑下,在UI Component中发出一个plain object的action,然后经过redux-saga这个中间件处理,redux-saga会将这个action传入相应channel,通过redux-saga的effect方法(比如call、put、apply等方法)生成一个描述对象,然后将这个描述对象转化成具有副作用的函数并执行。
在redux-saga执行具有副作用的函数时,又可以dispatch一个action,这个action也是一个plain object,会直接传入到redux的reducer函数中进行处理,也就是说在redux-saga的task中发出的action,就是同步的action。
简单的概括:从UI组件上发出的action经过了2层的处理,分别是redux-saga中间件和redux的reducer函数。
redux-dark模式很简单,就是将同一个子页面下的redux-saga处理action的saga函数,以及reducer处理该子页面下的state的reducer函数,放在同一个文件中。
举例来说:
import { connect } from 'react-redux'; class Hello extends React.Component{ componentDidMount(){ //发出一个action,获取异步数据 this.props.dispatch({ type:'async_count' }) } } export default connect({})(Hello);
从Hello组件中发出一个type = 'async_count'的action,我们用redux-dark模式来将saga和reducer函数放在同一个文件中:
import { takeEvery } from 'redux-saga/effects'; //saga function * asyncCount(){ console.log('执行了saga异步...') //发出一个原始的action yield put({ type:'async' }); } function * helloSaga(){ //接受从UI组件发出的action takeEvery('async_count',asyncCount); } //reducer function helloReducer(state,action){ if(action.type === 'count'); return { ...state,count + 1} }
上述就是一个将saga和reducer放在同一个文件里面的例子。redux-dark模式来组织代码,可以显得比较直观,统一了数据的处理层。分拆子页面后,每一个子页面对应一个文件。可读性很高。
上述的redux-dark模式,就是一种简单的处理,而dva的话是做了更近一步的封装,dva不仅封装了redux和redux-saga,还有react-router-redux、react-router等等。使得我们可以通过很简单的配置,就能使用redux、redux-saga、react-router等。
下面首先以dva的初始版本为例来理解一下dva的源码。
来看官网给的使用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) { //..处理监听等等函数 } ], }); // 3. View const App = connect(({ count }) => ({ count }))(function(props) { return ( <div> <h2>{ props.count }</h2> <button key="add" onClick={() => { props.dispatch({type: 'count/add'})}}>+</button> <button key="minus" onClick={() => { props.dispatch({type: 'count/minus'})}}>-</button> </div> ); }); // 4. Router app.router(({ history }) => <Router history={history}> <Route path="/" component={App} /> </Router> ); // 5. Start app.start(document.getElementById('root'));
只要三步就完成了一个例子,如何处理action呢,我们以一个图来表示:
也就是做UI组件上发出的对象类型的action,先去根据类型匹配=model初始化时候,effects属性中的action type。
在dva初始化过程中的effects属性中的函数,其实就是redux-saga中的saga函数,在该函数中处理直接的异步逻辑,并且该函数可以二次发出同步的action。
此外dva还可以通过router方法初始化路由等。
下面来直接读读dva 0.0.12的源码,下面的代码是经过我精简后的dva的源码:
//Provider全局注入store import { Provider } from 'react-redux'; //redux相关的api import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; //redux-saga相关的api,takeEvery和takeLatest监听等等 import createSagaMiddleware, { takeEvery, takeLatest } from 'redux-saga'; //react-router相关的api import { hashHistory, Router } from 'react-router'; //在react-router4.0之后已经较少使用,将路由的状态存储在store中 import { routerMiddleware, syncHistoryWithStore, routerReducer as routing } from 'react-router-redux'; //redux-actions的api,可以以函数式描述reducer等 import { handleActions } from 'redux-actions'; //redux-saga非阻塞调用effect import { fork } from 'redux-saga/effects'; function dva() { let _routes = null; const _models = []; //new dva暴露了3个方法 const app = { model, router, start, }; return app; //添加models,一个model对象包含了effects,reducers,subscriptions监听器等等 function model(model) { _models.push(model); } //添加路由 function router(routes) { _routes = routes; } function start(container) { let sagas = {}; //routing是react-router-redux的routerReducer别名,用于扩展reducer,这样以后扩展后的reducer就可以处理路由变化。 let reducers = { routing }; _models.forEach(model => { //对于每一个model,提取其中的reducers和effects,其中reducers用于扩展redux的reducers函数,而effects用于扩展redx-saga的saga处理函数。 reducers[model.namespace] = handleActions(model.reducers || {}, model.state); //扩展saga处理函数,sagas是包含了所有的saga处理函数的对象 sagas = { ...sagas, ...model.effects }; ---------------------------(1) }); reducers = { ...reducers }; //获取决定使用React-router中的那一个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来扩展reducers,同时生成扩展后的store实例 const store = app.store = createStore( combineReducers(reducers), initialState, enhancer ); // 执行model中的监听函数,监听函数中传入store.dispatch _models.forEach(({ subscriptions }) => { if (subscriptions) { subscriptions.forEach(sub => { store.dispatch, onErrorWrapper); }); } }); // 根据rootSaga来启动saga,rootSaga就是redux-saga运行的主task sagaMiddleware.run(rootSaga); //创建history实例子,可以监听store中的state的变化。 let history; history = syncHistoryWithStore(_history, store); --------------------------------(2) // Render and hmr. if (container) { render(); apply('onHmr')(render); } else { const Routes = _routes; return () => ( <Provider store={store}> <Routes history={history} /> </Provider> ); } 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(( <Provider store={store}> <Routes history={history} /> </Provider> ), container); } } } export default dva;
代码的阅读在上面都以注视的方式给出,值得注意的主要有一下3点:
const reducer = 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"的action。
在注释 (2) 处,通过react-router-redux的api,syncHistoryWithStore可以扩展history,使得history可以监听到store的变化。
在注释(3)处是一个rootSaga, 是redux-saga运行的时候的主Task,在这个Task中我们这样定义:
function* rootSaga() { for (let k in sagas) { if (sagas.hasOwnProperty(k)) { const watcher = getWatcher(k, sagas[k]); yield fork(watcher); } } }
从全局的包含所有saga函数的sagas对象中,获取相应的属性,并fork相应的监听,这里的监听常用的有takeEvery和takeLatest等两个redux-saga的API等。
总结:上面就是dva最早版本的源码,很简洁的使用了redux、redux-saga、react-router、redux-actions、react-router-redux等.其目的也很简单:
简化redux相关生态的繁琐逻辑
参考源码地址:https://github.com/dvajs/dva/tree/0.0.12
The text was updated successfully, but these errors were encountered:
可以,很强势!我最近在尝试自己搭建一个脚手架,了解下实现流程。但是在处理saga这块儿遇到了问题,能找到的绝大多是教程和文档都是一个简单的示例,只有一个rootSaga,在sotre.js里调用一下runSaga就好了,但是真实的项目里肯定会有好多saga文件,该怎么处理,一直没找到方法
Sorry, something went wrong.
统一引入saga文件,可以用redux-saga里面的fork和all函数。如果用想用cli的话,一种就是监听saga目录下所有saga文件的变化,另一种如果所有的saga在一个文件里,可以监听文件大小的变化
小姐姐你好,请问下,redux-saga中,怎么将saga.js拆分后合并一起export出去呢,我从你的前一篇文章来的
No branches or pull requests
React生态,dva源码阅读
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-dark模式
在使用redux和redux-saga的时候,特别是如何存放reducer函数和saga的generator函数,这两个函数是直接跟如何处理数据挂钩的。
回顾一下,在redux中使用异步中间件redux-saga后,完整的数据和信息流向:
在存在异步的逻辑下,在UI Component中发出一个plain object的action,然后经过redux-saga这个中间件处理,redux-saga会将这个action传入相应channel,通过redux-saga的effect方法(比如call、put、apply等方法)生成一个描述对象,然后将这个描述对象转化成具有副作用的函数并执行。
在redux-saga执行具有副作用的函数时,又可以dispatch一个action,这个action也是一个plain object,会直接传入到redux的reducer函数中进行处理,也就是说在redux-saga的task中发出的action,就是同步的action。
简单的概括:从UI组件上发出的action经过了2层的处理,分别是redux-saga中间件和redux的reducer函数。
redux-dark模式很简单,就是将同一个子页面下的redux-saga处理action的saga函数,以及reducer处理该子页面下的state的reducer函数,放在同一个文件中。
举例来说:
从Hello组件中发出一个type = 'async_count'的action,我们用redux-dark模式来将saga和reducer函数放在同一个文件中:
上述就是一个将saga和reducer放在同一个文件里面的例子。redux-dark模式来组织代码,可以显得比较直观,统一了数据的处理层。分拆子页面后,每一个子页面对应一个文件。可读性很高。
二、dva 0.0.12版本的使用和源码理解
上述的redux-dark模式,就是一种简单的处理,而dva的话是做了更近一步的封装,dva不仅封装了redux和redux-saga,还有react-router-redux、react-router等等。使得我们可以通过很简单的配置,就能使用redux、redux-saga、react-router等。
下面首先以dva的初始版本为例来理解一下dva的源码。
(1)、dva 0.0.12的使用
来看官网给的使用dva 0.0.12的例子:
只要三步就完成了一个例子,如何处理action呢,我们以一个图来表示:
也就是做UI组件上发出的对象类型的action,先去根据类型匹配=model初始化时候,effects属性中的action type。
在dva初始化过程中的effects属性中的函数,其实就是redux-saga中的saga函数,在该函数中处理直接的异步逻辑,并且该函数可以二次发出同步的action。
此外dva还可以通过router方法初始化路由等。
(2)、dva 0.0.12的源码阅读
下面来直接读读dva 0.0.12的源码,下面的代码是经过我精简后的dva的源码:
代码的阅读在上面都以注视的方式给出,值得注意的主要有一下3点:
INCREMENT和DECREMENT属性的函数就可以分别处理,type = "INCREMENT"和type = "DECREMENT"的action。
在注释 (2) 处,通过react-router-redux的api,syncHistoryWithStore可以扩展history,使得history可以监听到store的变化。
在注释(3)处是一个rootSaga, 是redux-saga运行的时候的主Task,在这个Task中我们这样定义:
从全局的包含所有saga函数的sagas对象中,获取相应的属性,并fork相应的监听,这里的监听常用的有takeEvery和takeLatest等两个redux-saga的API等。
总结:上面就是dva最早版本的源码,很简洁的使用了redux、redux-saga、react-router、redux-actions、react-router-redux等.其目的也很简单:
简化redux相关生态的繁琐逻辑
参考源码地址:https://github.com/dvajs/dva/tree/0.0.12
The text was updated successfully, but these errors were encountered: