dva是一个在redux和redux-saga的基础上封装的一个轻型框架,能辅助更好的组织代码进行开发。同时提供了react-router和fetch,基本上具备了开发web前端应用所需的主要工具,省下开发者自己进行配置安装的工作。
开始使用dva
首先使用dva-cli快速创建dva应用。
npm install dva-cli -g
使用dva快速创建新的项目框架。
dva new my-new-project
dva会默认使用tnpm, cnpm依赖安装,由于我现在的tnpm不能使用,只能把tnpm删掉。
问题:如何配置dva的默认npm源
项目结构
dva-cli会创建如下的初始化项目结构
.
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .roadhogrc.mock.js
├── .webpackrc // webpack 配置
├── mock
│ └── .gitkeep
├── package.json
├── public
│ └── index.html
└── src
├── assets
│ └── yay.jpg
├── components
│ └── Example.js
├── index.css
├── index.js
├── models
│ └── example.js // 数据(状态)管理中心
├── router.js // 路由
├── routes
│ ├── IndexPage.css
│ └── IndexPage.js // 路由中对应的页面入口
├── services
│ └── example.js
└── utils
└── request.js
接下来根据我的个人喜好对项目进行微调 * 加入pages目录,区别于components,前者用于放置页面的个性化组件,后者用于放置复用性强的公共组件。然而这种组织一定程度上使得routes的存在没有太大的意义。
在dva中使用redux
如果决定使用redux的话,需要在src/index.js
里加入要用的model
// 3. Model
app.model(require('./models/example1').default);
app.model(require('./models/example2').default);
在dva中使用redux-router
在router.js中将页面入口组件引入并配置相关的路由
import IndexPage from './routes/IndexPage';
import ManagerPage from './routes/ManagerPage';
import EntryPage from './routes/EntryPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/user" exact component={IndexPage} />
<Route path="/manager" exact component={ManagerPage} />
<Route path="/entry" exact component={EntryPage} />
</Switch>
</Router>
);
}
在model中使用redux-router
import { routerRedux } from 'dva/router';
put(routerRedux.push('/manager'))
routerRedux.push('/manager')
在组件中使用redux-router
import { routerRedux } from 'dva/router';
dispatch(routerRedux.push('/manager'))
接下来就可以愉快的开发了。
dva中的redux
首先,在使用dva开发时,可以将store拆分为多个model,每个model对应一个namespace,对于复杂的应用,这种组织方式更贴近逻辑,使得问题更为清晰。对比我之前在vue中将store拆为state, getters, mutations, actions要更科学一些。后者的查分方式可以成为“横向拆分”, 而前者的拆分方式则是一种“纵向拆分”。 这种纵向拆分使得每一个拆分出来的子集都是一个子服务的对应。该自己可以包含自己的state,reducers和effects,可以认为是一种前端微服务化的思想。
dva对store的构成与vuex相似,都将同步reducers和异步reducers拆分开来,以更好的管理监控异步操作。dva在异步处理上使用了redux-saga,借助Genarator来实现异步操作。
问题:为什么不使用async呢?是历史问题,还是二者在细节层面存在差别。
generator函数的使用
关于Generator Generator使用*
标识函数为Generator函数 使用yield
阻断异步操作,等到yield
后的表达式完成计算得到返回值时,再继续执行下面的代码。 关于是否要对同步操作使用yield
,我认为没有严格的限制。但如果其中存在你所关心的中间状态变量,可以加上yield
。这样就可以在调用next()
方法时在返回对象的value属性中查询到。
这点与async不同,async返回的是promise,而generator返回的则是状态对象
redux-saga中异步effects的函数有三个参数
*postFile({ payload }, {call, put}) {
// handle both create and update
let res = yield call(fetch, '//api.center/postFile', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
let result = yield res.json()
if (result.success) {
yield put({ type: 'getFileList' })
}
},
{ payload } = action
, 调用dispatch时所传递的参数{call, put}
call负责调用异步操作,put则调用其他reducers来完成对state的修改操作- 这与vuex不同,vuex中的action可以直接修改state(也可以调用mutations),但直接修改会导致action的逻辑过于复杂,失去了分段调试监控的优势。
call(异步操作函数,参数1,参数2,...)
return 异步函数的返回值(promise或值)put({type, payload})
与dispatch参数相同,但type中不需要定义命名空间
reducers的设计
在dva给出的实例代码中,有一个reducers的定义十分有趣
save(state, action) {
return { ...state, ...action.payload };
}
这种实现是对所有reducers的本质抽象,即修改state。在实践简单应用的开发时,基本上所有的reducers都可以只用这一个save来实现。那是不是无需编写其他的reducers了呢? 我认为不是的,对于如下情况,仍需对每一种情况编写对应的reducers 修改的状态(state)过多,导致难以通过修改值直接判断reducer所代表的逻辑是什么(在哪里调用,为什么调用)。这种情况下建议新定义reducer,对一个state的操作集进行封装,以便在调试时清楚被调用的reducer对应的业务逻辑是什么。 需要根据参数进行预计算,最终得到state。这时,建议将一些重要的逻辑运算迁移到reducer中,以方便管理或复用。
开放的webpack配置
.webpackrc使得可以根据个人需求对react进行配置而无需进行eject操作。 (也可以将.webpackrc改为.webpackrc.json从而使用json格式的配置) 如,我们可以配置babel插件来直接引入如antd或next之类的框架
{
"extraBabelPlugins": [
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": true
}]
]
}
如果不想使用css modules功能 (比如next的import配置失败,只能手动导入css),还可以加入
"disableCSSModules": true
不过,建议体验一下css modules的功能
import styles from './style.css';
<h1 className={styles.bgRed}>Hello React!</h1>
参考资料
Dva官网:https://dvajs.com/guide/
ES6 Generator:http://es6.ruanyifeng.com/#docs/generator
dva.js入门 https://www.jianshu.com/p/c7b3b9c98d04
antd配置:https://ant.design/docs/react/introduce-cn react eject:https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-eject
redux saga:https://redux-saga-in-chinese.js.org/