回顾
初识 react(一) 揭开 jsx 语法和虚拟 DOM 面纱
初识 react(二) 实现一个简版的 html+redux.JS 的 demo
初识 react(三)在 react 中使用 redux 来实现简版计数器
初识 react(四) react 中异步解决方案之 redux-saga
纠正下, 零配置是由 https://umijs.org/zh/guide/ 帮我们实现的, 不是 dva 帮我们做的, 但是 dva-cli 在下一个版本 (即 dva-cli 1.0) 已经内置了 umi, 简直开发者福音, 有兴趣朋友可以体验下最新版本.
- NPM i dva-cli@next -g // 安装下一代 dva-cli, 内置 umi
- dva new dvaTest
- cd dvaTest
- NPM start
我们这里还是从最基本的 dva 开始讲起, 了解流程. 重点: dva 并没有发明新的概念, 全都是以前提到的. 只是进行了一层封装, 对 redux,saga 中的概念很清楚的话, dva 就是白给你的, 没有难点, 不会来找我.
简介
基于 redux,redux-saga 和 react-router 的轻量级前端框架.
dva 是基于 react+redux 最佳实践上实现的封装方案, 简化了 redux 和 redux-saga 使用上的诸多繁琐操作
数据流向
数据的改变发生通常是通过:
用户交互行为(用户点击按钮等)
浏览器行为 (如路由跳转等) 触发的
当此类行为会改变数据的时候可以通过 dispatch 发起一个 action, 如果是同步行为会直接通过 Reducers 改变 State , 如果是异步行为 (副作用) 会先触发 Effects 然后流向 Reducers 最终改变 State.
实现的 demo 效果
由于 dva 比较简单, 没有什么新概念用例子讲解会更明白. 最后要实现一个异步获取数据 num, 然后点击计数器 + num 的效果
目录结构
1, 主入口文件
- import React from 'react';
- import dva from 'dva';
- import Counter from './Counter';
- //dva 是一个函数, 通过执行它可以拿到一个 App 对象
- let App = dva();
- // 一个模板就是一个状态, 然后把 reducer 和状态 写在一起了,
- // 添加一个模块
- App.model({
- xxxx
- });
- // 参数是一个函数, 此应用本身就是要渲染函数的返回值
- App.router(() => <Counter />);
- // 本质是启动应用, 就是通过 App.router 获取组件, 并且通过 ReactDOM 渲染到容器内容
- App.start('#root');
以上是最基本的 dva 的主入口文件, 简单的 3 个 API,App.model,App.router,App.start, 就已经讲 react,redux-router,redux,redux-saga 整合一起, 简直开发者福音. 我们讲解下怎么用
dva 是一个函数, 通过执行它可以拿到一个 App 对象
App.model()添加一个模块, 下面重点讲解
App.router()接受函数, 然后渲染函数返回值
App.start('#root'), 通过 App.router 获取组件, 然后通过 ReactDom 渲染到容器
1.1,App.model()用法
接受一个对象, 把 state,reducers,effects 全部写在这, 便于维护.
- App.model({
- // 命名空间. 因为一个应用会有很多个模型, 每个模型要有一个名字
- namespace: 'counter',
- // 此命名空间的默认状态
- state: { current: 0, highest: 0 },
- // 它是用来接收 action, 修改仓库状态的
- reducers: {
- save(state, action) {
- return { current:state.current+action.payload };
- }
- }
- });
看见这些名词应该很熟悉吧
namespace 命名空间, 我们需要给模型一个名字
state=>状态, 就是 redux 中的状态
reducers=>处理器, 就是 redux 中的处理器
在强调遍, dva 没有发明新的概念, 只是进行了一层封装. 让状态更利于维护
redux 中概念还不清楚看这里
2, 编写 Counter.JS 组件
- import React from 'react';
- import { connect } from 'dva';
- class Counter extends React.Component {
- render() {
- return (
- <div className="container">
- <div className="current">
当前记录:{this.props.current}
- </div>
- <div className="addButton">
- <button onClick={() => this.props.dispatch({ type: 'counter/save',payload:2 })}>+</button>
- </div>
- </div>
- )
- }
- }
- export default connect(
- state => {
- return state.counter;
- }
- )(Counter);
不过多解释, 有 2 个地方需要注意:
组件内部派发动作时, type:'counter/add', 前面多了 counter(命名空间)
connect 时的状态是总的状态, 需要制定下需要 counter 的状态
目前为止, dva 流程已经跑通了, 是不是很简单, 我们测试下是否能点击加 2
完美实现, 说好的异步呢, 接下来我们用 express 编写一个简单接口
3, 编写服务端接口 server.JS
我们用 express 编写简单接口, 不讲解 express 用法. express 直通车 http://www.expressjs.com.cn/
- let express = require('express');
- let cors = require('cors'); // 解决跨域的包
- let App = express();
- App.use(cors()); // 使用中间件 cors
- App.get('/amount', function (req, res) {
- res.send({ amount: 5 });
- });
- App.listen(3000);
接下来启动服务, 看下效果
4, 客户端发请求获取数据
由于案例比较简单, 都写在了 src/index.JS 中
- function getAmount() {
- return fetch('http://localhost:3000/amount', {
- headers: {
- "Accept": "application/JSON"
- }
- }).then(res => res.JSON());
- }
5, 在 App.model 中添加 effects(副作用) (就是 redux-saga 中的 effects)
- effects: {
- // 表示这是一个 generator effect=redux-saga/effects
- *add(action, { call, put }) {
- let { num } = yield call(getAmount);
- yield put({ type: 'save', payload: num });
- }
- },
先异步获取数据, 然后再派发动作修改状态, 接着刷新视图
6, 对应的组件新增一个异步记数的按钮
- <button onClick={()=>
- this.props.dispatch({ type: 'counter/save',payload:2 })}>同步加 2
- </button>
- <button onClick={()=>
- this.props.dispatch({ type: 'counter/add' })}>异步记数
- </button>
增加了一个异步计数按钮, 会派发 add 动作类型.
add 类型被 effects(副作用)中的 add 监听到, 执行 getAmount()异步获取数据
拿到数据后派发 save 动作, 被 reducers 处理
页面刷新
测试结果
完结
dva 简化了 redux 和 redux-saga 使用上的诸多繁琐操作, 便于我们开发, 可维护性也更高, 配合 umi 使用, 号称零配置, 下篇文章会讲解 dva+umi 使用
如果对您有帮助, 点个喜欢再走呗
更多优质文章参考 https://chaiguanpeng.GitHub.io/
redux 所有源码解析戳这里
来源: https://juejin.im/post/5ba3183cf265da0ac726f1d3