Dva整体理解
前言
README.md
欢迎阅读,本文档将带你了解dva的整体思路。
介绍
应用 dva
框架搭建平台,dva
是基于 redux
最佳实践实现的framework,简化使用 redux
和redux-saga
时很多繁杂的操作。 dva
文档比较健全,可以访问https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/01-%E6%A6%82%E8%A6%81.md查看更多内容,其中会介绍整个框架的开发思路。
关于dva
dva
是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装;
数据流向: 数据的改变发生通常是通过:
- 用户交互行为(用户点击按钮等)
- 浏览器行为(如路由跳转等)触发的
当此类行为会改变数据的时候可以通过 dispatch
发起一个 action
,如果是同步行为会直接通过Reducers
改变 State
,如果是异步行为(副作用)会先触发 Effects
然后流向 Reducers
最终改变State
。 所以在 dva
中,数据流向非常清晰简明,如上图。
关于Redux-saga
Redus-saga
是一个 redux
的中间件,主要用来简便而优雅的处理 redux
应用里的副作用(side effect相对于pure function这类概念而言的)。它之所以可以做到这一点主要是使用了ES6
里的一个语法: Generator
。使用 Generator
可以像写同步的代码一样编写异步代码,这样更加容易测试。
准备
本文档第一部分将带领大家获得项目源码。首先需要的是将 gitlad
上的远程项目仓库克隆到本地;
$ git clone http://gitlab.xxxx.com/xx/xxx.git
克隆完成后,本地目标文件夹里就会出现目标远程仓库的文件;
目录
本文档的第一部分将带领大家了解dva项目 目录结构。在下载源码之后,我们可以看到,整个系统的构建以及管理。目录如下:
工程结构
如下:
● xxxx
○ public
○ src #项目源码目录
■ assets #静态资源
■ common #路由资源
■ components #项目组件
■ layouts #项目布局组件
■ models #数据模型
■ routes #路由组件(页面维度)
■ services #数据接口
■ utils #工具函数
■ index.js #入口文件
■ router.js #路由配置
开始
增加一个新功能
在上面的介绍中中我们已经对 dva
有了一定的认识,接下来我们会一起添加一个较为完善的功能,在实现的过程中,我们逐步完成以下内容:
- 添加顶层路由数据
- 添加路由
- 添加布局
- 定义service
- 设计Model
- 组件设计
添加顶层路由数据
第一步,我们会按照功能在 ./common/nav.js
添加路由数据:(部分代码)
// common/nav.js
{
name: 'xxx',
path: 'aaa',
icon: 'global',
page: 'ppp',
grayscale: true,
children: [
{
component: dynamicWrapper(app, ['aaa'], () =>
import('../routes/aaa'))
},
//......
],
}
定义路由
路由决定进入 url
渲染哪些 Component
。 history
默认是 hashHistory
;
// router.js
return (
<LocaleProvider locale={zhCN}>
<Router history={history}>
<Switch>
<Route path="/login" render={props => <XxxLayout {...props}
{...passProps} />} />
<Route path="/sss" component={sss} />
<Redirect exact from="/" to="/index" />
</Switch>
</Router>
</LocaleProvider>
);
设计布局
路由添加完成后,设计布局,这些布局内容被抽象成组件,包含一些布局样式,用于组合其它组件搭建成页面。
本质上还是一种组件,将布局样式抽象成组件,能够保持子组件和父组件的独立性,不用在其中关联到布局信息。
注:若添加的是二级功能,不需要添加新的布局
定义service
接下来就是将请求相关抽离出来,单独放到 /services/
中,进行统一维护管理;
如:
// services/xxx.js
export async function xxx(params) {
return request(`xxx.json`, {
method: 'GET',
});
}
设计Model
在完成项目基本的前期以后,我们将要开始设计 model
,在设计 model
之前,我们需要回顾一下项目的功能;
我们可以看出,这部分功能基本是围绕 以用户数据为基础 的操作,其中包含:
- 用户信息的展示
- 用户信息的操作
无论是多复杂的项目也基本上是围绕着数据的展示和操作,复杂一点的是组合了很多数据,有关联关系,只要分解开来, model
的结构层次依旧会很清晰,便于维护。
所以抽离 model
的原则就是抽离数据模型。
export default {
namespace: 'xxx',
state: {
helps: [],
task: {},
data: {
list: [],
pagination: {
current: 1,
},
},
},
effects: {
* auth() {},
* list() {};
* detail() {},
* xxList() {};
* xxOperation() {},
},
reducers: {
save(){}
},
};
model
包含了关于操作的 state
,处理异步的 effect(dispatch action)
,以及修改 state
的reducers
;
视图设计
数据设计完成后,我们着手新增组件的编写,在 ./routes
中添加新增组件,根据 @connect
包裹组件的 props
进行操作的编写:
@connect(({ a1, a2 }) => ({
a1,
a2: ......,
}))
export default class xxx extends PureComponent {
state = {
page: 1,
type: 1,
//.....
}
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'xx/auth',
});
dispatch({
type: 'xx/list',
payload: {
page: 1,
type: 1,
},
});
}
组件设计
在确定了大体的设计方法以后,让我们来看看如何设计 dva
中的 React
组件。React
应用是由一个个独立的 Component
组成的,我们在拆分 Component
的过程中要尽量让每个Component
专注做自己的事。
对组件分类,主要有两个好处:
- 让项目的数据处理更加集中;
- 让组件高内聚低耦合,更加聚焦;
注:本平台使用
ant design
进行组件的设计,避免样式表的复杂编写;
写在最后
dva
将所有与数据操作相关的逻辑集中放在一个地方处理和维护,在数据跟业务状态交互比较紧密的场景下,会使我们的代码更加清晰可控。
对于一个大型管理系统,由于要进行大量的数据操作,在设计model时将不同类型的业务需求数据操作分开处理,便于维护。
项目的开发流程一般是从设计 model state
开始进行抽象数据,完成 component
后,将组件和model
建立关联,通过 dispatch
一个 action
,在 reducer
中更新数据完成数据同步处理;当需要从服务器获取数据时,通过 Effects
数据异步处理,然后调用 Reducer
更新全局 state
。是一个单向的数据流动过程。解决了 redux
中代码分散和重写问题,总之,Dva:Build redux application easier and better。
官方地址
Ant Design
Redux-saga 中文文档
dva-knowledgemap
dva: react application arch in ant financial