Dva.js 快速上手指南
先说些废话
最近在开发React技术栈的项目产品,对于数据状态的管理使用了Dva.js,作为一个资深的ow玩家,我看到这个名字第一反应就是————这不是ow里的一个女英雄吗?仔细阅读了官方文档之后,发现开发者还真是因为这个角色获得灵感,来命名这个数据状态管理插件,果然开发大佬都是工作和休闲两不误~
学过React的同学都知道它的技术栈非常多且杂,所以每当你使用React的时候都需要引入很多的模块,那么Dva就是把这些用到的模块集成在一起,比如一些需要引入的依赖react-saga/react-loger、必写的ReactDOM.render、provider、connect包裹等都省去不写,形成一定的架构规范,大大提高我们的开发效率
今天,就来写一份文档,帮助后续使用Dva的开发者更好得在实际项目中(PS:需要是以UMI为基础框架,纯Dva来构建项目可以直接看文章结尾的参考文档列表)上手使用
什么是Dva
Dva首先是一个基于redux和redux-saga的数据流方案,然后为了简化开发体验,Dva还额外内置了react-router和fetch,所以也可以理解为一个轻量级的应用框架。
在我目前的项目中,更多是使用数据状态管理的功能,他在我司的fish框架中做了内嵌,在主流的React开发框架UMI中也做了内嵌适配,使用起来非常方便快速。
Dva设计的目的就是简化元素,降低难度,让你不用管他怎么实现的,我们按照默认的这个规则去写就可以
数据流向
数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过dispatch发起一个action,如果是同步行为会直接通过reducers改变states,如果是异步行为(副作用)会先触发effects然后流向reducers最终改变states

分层开发
无论是Vue还是React开发,实际的大型应用一定有严格的分层开发规范,确保后续开发的可维护性,主要的分层结构有以下几点:
- Page 负责与用户直接打交道:渲染页面,接受用户的操作输入,侧重于展示型交互性逻辑,这里需要了解无状态组件
- Model 负责处理业务逻辑,为 Page 做数据、状态的读写、变换、暂存等,
Dva中model就是做了这一层的操作 - Service 负责与 HTTP 接口对接,进行纯粹的数据读写
基础概念
- namespace
model的命名空间,同时也是他在全局state上的属性- 只能用字符串,不支持通过
.的方式创建多层命名空间,相当于这个model的key - 在组件里面,通过
connect这个key将想要引入的model加入
import { connect } from 'dva';
export default connect(({ namespaceValue }) => ({ ...namespaceValue }))(DvaCompoent);
- state
- 表示
model的状态数据 - 操作的时候每次都要当作不可变数据
immutable data来对待,保证每次都是全新对象,没有引用关系
- 表示
- reducer
- 必须是纯函数,有固定输入输出,主要目的是修改自身
state - 接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果,该函数把一个集合归并成一个单值
- 需要注意的是同样的输入必然得到同样的输出,它们不应该产生任何副作用
effect。并且,每一次的计算都应该使用immutable data
- 必须是纯函数,有固定输入输出,主要目的是修改自身
- effect
- 主要用于异步请求,接口调用之类的
effect被称为副作用,在我们的应用中,最常见的就是异步操作- 它来自于函数编程的概念,之所以叫副作用是因为它使得我们的函数变得不纯,同样的输入不一定获得同样的输出
- subscription
subscription语义是订阅,用于订阅一个数据源,然后根据条件dispatch需要的action- 数据源可以是当前的时间、服务器的websocket连接、keyboard输入、geolocation变化、history路由变化等等
- 内部定义的函数都会被被执行,执行之后作为监听来处理事务
- dispatch
dispatch是一个用于触发action的函数,action是改变state的唯一途径,但是它只描述了一个行为,而dipatch可以看作是触发这个行为的方式,reducer则是描述如何改变数据的- 在
Dva中,connect model的组件通过props可以访问到dispatch,可以调用model中的reducer或者effects
import { connect } from 'dva';
const testCom = props => {
const { dispatch } = props;
const changeValue = (id, val) => {
// 调用reducer,一般是同步修改state中的值
dispatch({
type: 'dva/save',
payload: {
param: val
},
});
// 调用effect,一般是发送后台请求
dispatch({
type: 'dva/queryValue',
payload: {
id: id
},
});
};
return(
<div>'hello world'</div>
)
}
export default connect(({ dva }) => ({ ...dva }))(testCom);
Model中的Effects函数解析
需要注意的是:Effects里面的函数都是Generator函数
- yield
- 固定关键词,
Generator函数自带的关键词,和*搭配使用,有点像async和await,使用*则表明它是Generator函数 - 然后每使用一个
yield就是告诉程序这里是异步,需要等待这个后面的代码执行完成,同步代码可不使用该关键词
- 固定关键词,
- payload
- 页面上通过
dispatch传过来的payload同名参数
- 页面上通过
- select
Dva中Effects函数的固定传参- 用于拿到
model中state的数据,需要注意的是,state后面跟命名空间namespace的值
const data = yield select((state) => state.namespaceName.valueName);
- call
Dva中Effects函数的固定传参- 第一个参数是一个异步函数,
payload是参数,可以通过call来执行一个完整的异步请求,又因为yield的存在,就实现了异步转同步的方案
const { data } = yield call(queryInterface, payload);
- put
Dva中Effects函数的固定传参- 可以使用同
model中的Reducers或者Effects,通过Reducers来实现数据到页面的更新,也可以通过put实现Effects的嵌套使用
yield put({
type: 'save',
payload: {
...payload
},
});
开发目录
由于公司的fish框架以及常见的umi框架都对Dva做了深度继承,会默认将src/models下的model定义自动挂载,只需要在model文件夹中新建文件即可新增一个model用来管理组件状态,对于某个page文件夹下面的model也会默认挂载
├─assets `静态资源`
├─components `公共组件`
├─config `路由和环境配置`
├─constants `全局静态常量`
├─locale `国际化`
│ ├─en_US `英文配置`
│ └─zh_CN `中文配置`
├─models `全局数据状态` *Dva涉及的目录*
├─pages `页面目录,用我参与开发的其中一个目录来作为示例` *Dva涉及的目录*
│ ├─NodeConfig `NodeConfig示例目录`
│ │ ├─components
│ │ │ ├─Select `Select组件页面文件` *Dva涉及的目录*
│ │ │ │ └─components
│ │ │ │ ├─AudienceInfo
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─BlackList
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─ControlGroup
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ └─GroupSelect
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─index.js
│ │ │ │ └─index.less
│ │ ├─models
│ │ │ ├─select.js `Select组件数据状态管理` *Dva涉及的目录*
│ │ └─services
├─services `全局接口配置`
├─themes `全局样式主题`
└─utils `js通用工具`
PS: 该树形图通过 `windows shell` 自带的 `tree` 命令生成
如何使用Dva
首先定义一个简易的model示例
export default {
namespace: 'dva',
state: {
id: '',
value: {},
},
effects: {
// 所有effect前必须要加 *
*queryValue({ payload }, { select, call, put }) {
const params = {
id: payload.id ? payload.id : yield select(state => state.select.id)
}
const { data } = yield call(queryInterface, params); // queryInterface是定义好的后台请求接口,一般用axios或fetch来完成
yield put({ type: 'save', payload: data });
},
},
reducers: {
save(state, { payload }) {
return {
...state,
...payload,
};
},
},
subscriptions: {
keyboardWatcher({ dispatch }) {
key('⌘+up, ctrl+up', () => { dispatch( {type:'save'}) });
},
},
};
然后把model和组件绑定在一起
React的Connect函数是一种柯里化写法
import { connect } from 'dva';
const testCom = props => {
const { helloWorld = 'hello world'} = props;
return(
<div>{ helloWorld }</div>
)
}
// 绑定之后就可以在testCom组件中使用命名为dva的model了
export default connect(({ dva }) => ({ ...dva }))(testCom);
柯里化
柯里化是把接受多个参数的函数转换成接受一个单一参数的函数(PS:Scala语言中也有类似的设计)
// 柯里化
var foo = function(x) {
return function(y) {
return x + y
}
}
foo(3)(4)
// 普通方法
var add = function(x, y) {
return x + y;
}
add(3, 4)
无状态组件
创建无状态组件是为了创建纯展示组件,这种组件只负责根据传入的props来展示,不涉及到要改变state状态的操作,
在实际项目中页面组件被写成无状态的组件,通过简单组合可以构建成页面或复杂组件,通过多个简单组件来合并成一个复杂的大应用
const NoStateComponent = props => {
const { helloWorld = 'hello world'} = props;
return(
<div>{ helloWorld }</div>
)
}
export default NoStateComponent;
无状态组件的优点
- 由于是无状态组件,所以无状态组件就不会在有组件实例化的过程,无实例化过程也就不需要分配多余的内存,从而性能得到一定的提升
- 代码整洁、可读性高,对于大型项目的开发维护非常有好处
参考文档一 ———— Dva官方文档
参考文档二 ———— UMI官方文档
参考文档三 ———— REACT基础笔记 MODEL分层
参考文档四 ———— 前端数据流方案Dva
参考文档五 ———— 浅析dva (史上最全的dva用法及分析)
参考文档六 ———— 【dva】model中effects函数的解析
参考文档七 ———— Generator 函数的详解
参考文档八 ———— React connect()() 双括号 --柯里化写法
参考文档九 ———— 高级函数技巧-函数柯里化
我是 fx67ll.com,如果您发现本文有什么错误,欢迎在评论区讨论指正,感谢您的阅读!
如果您喜欢这篇文章,欢迎访问我的 本文github仓库地址,为我点一颗Star,Thanks~
转发请注明参考文章地址,非常感谢!!!
Dva.js 快速上手指南的更多相关文章
- Rancher 快速上手指南操作(1)
Rancher 快速上手指南操作(1)该指南知道用户如何快速的部署Rancher Server 管理容器.前提是假设你的机器已经安装好docker了.1 确认 docker 的版本,下面是 ubunt ...
- UnityShader快速上手指南(三)
简介 这一篇还是一些基本的shader操作:裁剪.透明和法向量的应用 (纠结了很久写不写这些,因为代码很简单,主要是些概念上的东西) 先来看下大概的效果图:(从左到右依次是裁剪,透明,加了法向量的透明 ...
- [转]Rancher 快速上手指南操作(1)
本文转自:http://www.cppblog.com/zhiyewang/archive/2016/03/17/213053.aspx Rancher 快速上手指南操作(1)该指南知道用户如何快速的 ...
- Markdown快速上手指南
Markdown快速上手指南 1.Markdown介绍 markdown可以实现快速html文档编辑,格式优没,并且不需要使用html元素. markdown采用普通文本的形式,例如读书笔记等易于使用 ...
- Ficow 的 AI 平台快速上手指南(ChatGPT, NewBing, ChatGLM-6B, cursor.so)
本文首发于 Ficow Shen's Blog,原文地址: Ficow 的 AI 平台快速上手指南(ChatGPT, NewBing, ChatGLM-6B, cursor.so). 内容概览 前言 ...
- Github Action 快速上手指南
前言 各位读者,新年快乐,我是过了年匆忙赶回上海努力搬砖的蛮三刀. Github之前更新了一个Action功能(应该是很久以前了),可以实现很多自动化操作.用来替代用户自己设置的自动化脚本(比如:钩子 ...
- UnityShader快速上手指南(二)
简介 前一篇介绍了如果编写最基本的shader,接下来本文将会简单的深入一下,我们先来看下效果吧 呃,gif效果不好,实际效果是很平滑的动态过渡 实现思路 1.首先我们要实现一个彩色方块 2.让色彩动 ...
- UnityShader快速上手指南(一)
简介 引言 其实网上有很多shader教程,但是大概看了下,也不知是网上各位大神已经脱离了代码层面的高度还是啥原因.貌似没有找到从代码方面作为入门讲解的,导致了shader对于苦逼程序员入门有一定要求 ...
- Mac快速上手指南
上周刚入手了2017版MacBookPro,预装macOS High Sierra.第一次接触Mac系统,经过一周的使用,简单总结下与Windows相比最常用的功能,快速上手. 1.Mac键盘实现Ho ...
- IDEA 快速上手指南(全配置)(Day_23)
Idea快速入门指南 1.安装 1.1.安装 我们使用的是2017.3.4版本: 双击打开, 选择一个目录,最好不要中文和空格: 然后选择桌面快捷方式,请选择64位: 然后选择安装: 开始安装: 然后 ...
随机推荐
- 纯CSS实现带小三角提示框
要实现在页面上点击指定元素时,弹出一个信息提示框.在前面的文章中,我们已经简单介绍了如何使用纯 CSS 创建一个三角形.本文在此基础上,记录如何使用 CSS 创建带三角形的提示框. 实现的原理是创建一 ...
- javascript现代编程系列教程之六——数字型数据类型转换
一.整数转换 在 JavaScript 中,parseInt() 函数会将其参数转换为字符串,然后解析该字符串,并返回一个整数或 NaN.如果 parseInt() 函数的参数是一个非常大的浮点数(如 ...
- javascript现代编程系列教程之三——数值表示方式
在64位浮点数的表示中,我们使用了一个叫做"偏移二进制"的技术来存储指数.这是因为我们需要在有限的位数(在这种情况下是11位)内表示正数和负数. 让我们更详细地解释一下: 符号位: ...
- 网易数帆Curve加入PolarDB开源数据库社区
简介:Curve社区签署阿里巴巴开源CLA(Contribution License Agreement, 贡献许可协议), 正式与阿里云PolarDB 开源数据库社区牵手. Curve社区签署阿里 ...
- MaxCompute在电商场景中如何进行漏斗模型分析
简介: 本文以某电商案例为例,通过案例为您介绍如何使用离线计算并制作漏斗图. 背景 漏斗模型其实是通过产品各项数据的转化率来判断产品运营情况的工具.转化漏斗则是通过各阶段数据的转化,来判断产品在哪一个 ...
- Jaeger插件开发及背后的思考
简介: 本文主要介绍Jaeger最新的插件化后端的接口以及开发方法,让大家能够一步步的根据文章完成一个Jaeger插件的开发.此外SLS也推出了对于Jaeger的支持,欢迎大家试用. 随着云原生 + ...
- [FAQ] WPS 服务程序是一种流氓软件吗
是的,周而复始的后台进程,频率大致是每隔一个小时会运行一个购物车图标的后台程序,点击之后就会打开电商网站,随后这个程序消失.再出现. 当前时间:2021-10-29 Other:[FAQ] 你所看过的 ...
- 修改python别名
修改Python别名 发现课程提供的启动python程序的命令均为python3,而我们的环境中只能用python来启动 Python 解释器,可以通过修改python的别名来实现不同名称调用同一个程 ...
- C# 二进制数组与结构体的互转
本文将告诉大家在 dotnet 里面的二进制基础处理知识,如何在 C# 里面将结构体数组和二进制数组进行相互转换的简单方法 尽管本文属于基础入门的知识,但是在阅读之前还请自行了解 C# 里面的结构体内 ...
- 阿里云边缘容器云帮助AI推理应用快速落地
近日,阿里云技术专家徐若晨在全球分布式云大会上,分享了<边缘容器云助力AI推理高效落地>的主题演讲,分享了阿里云边缘容器云如何助力开发者实现更快速的AI推理应用的迭代和部署.此外,他还分享 ...