009 Umi 中的 elm 概念的数据流管理方案 Dva

简介: 009 Umi 中的 elm 概念的数据流管理方案 Dva

image.png


虽然 Umi 中的 dva 已经不是官方推荐的最好的数据流管理方案了,但是学习 dva 的时候,其实更有利于我们后面熟悉纯 hooks 的数据流管理方案。


在 Umi 中我们对请求方法做了高效的封装,对开发中遇到的请求相关的服务都做了内置功能。

比如 mock 数据、请求代理、统一请求地址配置、接口文件组织等都有鲜明的 Umi 风格。


在接下来的几个课程中,我们会详细的说明,Umi 在数据获取方面提供的能力和服务。

Umi 将如何帮助你高效的完成数据获取和绑定的工作。

学完这节课您将会掌握 dva 的基本入门。


dva

什么是 dva?

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

通常在 dva 的项目中,你需要掌握 (6 个 API](dvajs.com/guide/conce… redux 的概念已经很少了,但是在 Umi 项目中,你需要掌握的 API 数是 0。 即一个也不需要掌握。因为在 Umi 中通过约定的方式组织代码,框架自动完成了相应 API 的执行。

比如,在 src/models 中新建文件,就会被自动使用 app.model 绑定到 dva 中。



为什么要用 dva

经过一段时间的自学或培训,大家应该都能理解 redux 的概念,并认可这种数据流的控制可以让应用更可控,以及让逻辑更清晰。

但随之而来通常会有这样的疑问:概念太多,并且 reducer, saga, action 都是分离的(分文件)。


这带来的问题是:

  • 编辑成本高,需要在 reducer, saga, action 之间来回切换
  • 不便于组织业务模型 (或者叫 domain model) 。比如我们写了一个 userlist 之后,要写一个 productlist,需要复制很多文件。

还有一些其他的:

  • saga 书写太复杂,每监听一个 action 都需要走 fork -> watcher -> worker 的流程
  • entry 书写麻烦
  • ...

而 dva 正是用于解决这些问题。


什么时候需要使用 dva

在 react hooks 上线之后。在 Umi 项目中,我们建议轻量的使用 dva。仅仅在以下几种场景下推荐使用 dva:

  1. 父子组件之间的数据互通
  2. 多页面之间的数据传递(即,公共数据)
  3. 非 react 组件的场景



dva 入门课

::: tip 内容来自之前为阿里内部同学准备的入门课。根据 Umi 中的使用情况,对无需关注的概念做了删减。 :::


React 没有解决的问题

React 本身只是一个 DOM 的抽象层,使用组件构建虚拟 DOM。

如果开发大应用,还需要解决一个问题。

  • 通信:组件之间如何通信?
  • 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等



通信问题

组件会发生三种通信。

  • 向子组件发消息
  • 向父组件发消息
  • 向其他组件发消息

React 只提供了一种通信手段:传参。对于大应用,很不方便。



组件通信的例子

步骤 1

class Son extends React.Component {
  render() {
    return <input />;
  }
}
class Father extends React.Component {
  render() {
    return (
      <div>
        <Son />
        <p>这里显示 Son 组件的内容</p>
      </div>
    );
  }
}
ReactDOM.render(<Father />, mountNode);
复制代码

看这个例子,思考一下父组件如何拿到子组件的值。


步骤 2

class Son extends React.Component {
  render() {
    return <input onChange={this.props.onChange} />;
  }
}
class Father extends React.Component {
  constructor() {
    super();
    this.state = {
      son: '',
    };
  }
  changeHandler(e) {
    this.setState({
      son: e.target.value,
    });
  }
  render() {
    return (
      <div>
        <Son onChange={this.changeHandler.bind(this)} />
        <p>这里显示 Son 组件的内容:{this.state.son}</p>
      </div>
    );
  }
}
ReactDOM.render(<Father />, mountNode);
复制代码

看下这个例子,看懂源码,理解子组件如何通过父组件传入的函数,将自己的值再传回父组件。


数据流图

image.png


核心概念

  • State:一个对象,保存整个应用状态
  • View:React 组件构成的视图层
  • Action:一个对象,描述事件
  • connect 方法:一个函数,绑定 State 到 View
  • dispatch 方法:一个函数,发送 Action 到 State


State 和 View

State 是储存数据的地方,收到 Action 以后,会更新数据。

View 就是 React 组件构成的 UI 层,从 State 取数据后,渲染成 HTML 代码。只要 State 有变化,View 就会自动更新。


Action

Action 是用来描述 UI 层事件的一个对象。

{
  type: 'click-submit-button',
  payload: this.form.data
}
复制代码


connect 方法

connect 是一个函数,绑定 State 到 View。也支持高阶函数的用法。

import { connect } from 'dva';
function mapStateToProps(state) {
  return { todos: state.todos };
}
connect(mapStateToProps)(App);
复制代码


connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。

connect 方法传入的第一个参数是 mapStateToProps 函数,mapStateToProps 函数会返回一个对象,用于建立 State 到 Props 的映射关系。


dispatch 方法

dispatch 是一个函数方法,用来将 Action 发送给 State。

dispatch({
  type: 'click-submit-button',
  payload: {},
});
复制代码


dispatch 方法从哪里来?被 connect 的 Component 会自动在 props 中拥有 dispatch 方法。

connect 的数据从哪里来? connect 方法传入的第一个参数是 mapStateToProps 函数,该函数默认传入一个参数 state 对应了整个应用的 state,你可以通过设置映射关系,将 state 中的某个值,绑定到页面组件的 props 里面。



数据流图

image.png


model 最简结构

export default {
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) {
      return state + 1;
    },
  },
  effects: {
    *addAfter1Second(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    },
  },
};
复制代码


Model 对象的属性

  • namespace: 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
  • state: 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
  • reducers: Action 处理器,处理同步动作,用来算出最新的 State
  • effects:Action 处理器,处理异步动作


Reducer

Reducer 是 Action 处理器,用来处理同步操作,可以看做是 state 的计算器。它的作用是根据 Action,从上一个 State 算出当前 State。

一些例子:

// count +1
function add(state) {
  return state + 1;
}
// 往 [] 里添加一个新 todo
function addTodo(state, action) {
  return [...state, action.payload];
}
// 往 { todos: [], loading: true } 里添加一个新 todo,并标记 loading 为 false
function addTodo(state, action) {
  return {
    ...state,
    todos: state.todos.concat(action.payload),
    loading: false,
  };
}
复制代码


Effect

Action 处理器,处理异步动作,基于 Redux-saga 实现。Effect 指的是副作用。根据函数式编程,计算以外的操作都属于 Effect,典型的就是输入输出操作,全局 dom 变化,访问服务端数据等。

function* addAfter1Second(action, { put, call }) {
  yield call(delay, 1000);
  yield put({ type: 'add' });
}
复制代码


Generator 函数

Effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)。


call 和 put

dva 提供多个 effect 函数内部的处理函数,比较常用的是 callput

  • call:执行异步函数
  • put:发出一个 Action,相当于 View 里面的 dispatch

到这里,我们就将 dva 的基本概念讲解清楚了,如果你不是很理解,建议你多看几遍。如果你稍微有了一点概念,那就可以在后续的课程中慢慢掌握以上所有概念。这样你会更加清楚的了解到如何在项目中使用 dva。

感谢阅读,今天不需要写任何的代码,只需要简单的搞懂 dva 的概念即可。我们会在后续手动创建 dva 插件来完成上述提到的,为什么 dva 中的 6 个概念,正在的业务开发中可以一个都不用掌握。

目录
相关文章
|
3月前
|
前端开发 UED 开发者
React组件优化全攻略:深度解析让你的前端应用飞速运行的秘诀——从PureComponent到React.memo的彻底性能比较
【8月更文挑战第31天】在构建现代Web应用时,性能是提升用户体验的关键因素。React作为主流前端库,其组件优化尤为重要。本文深入探讨了React组件优化策略,包括使用`PureComponent`、`React.memo`及避免不必要的渲染等方法,帮助开发者显著提升应用性能。通过实践案例对比优化前后效果,不仅提高了页面渲染速度,还增强了用户体验。优化React组件是每个开发者必须关注的重点。
64 0
|
3月前
|
缓存 JavaScript 前端开发
【React生态进阶】React与Redux完美结合:从原理到实践全面解析构建大规模应用的最佳策略与技巧分享!
【8月更文挑战第31天】React 与 Redux 的结合解决了复杂状态管理的问题,提升了应用性能。本文详细介绍了在 React 应用中引入 Redux 的原因、步骤及最佳实践,包括安装配置、状态管理、性能优化等多方面内容,并提供了代码示例,帮助你构建高性能、易维护的大规模应用。
55 0
|
6月前
|
前端开发 JavaScript
详解React:Props构建可复用UI的基石
详解React:Props构建可复用UI的基石
39 0
|
资源调度 前端开发 API
用 React 构建可复用的设计系统(一)
用 React 构建可复用的设计系统
106 0
|
前端开发 JavaScript
组件与Props:React中构建可复用UI的基石
组件与Props:React中构建可复用UI的基石
81 0
|
前端开发 JavaScript API
React-高阶组件-应用场景
React-高阶组件-应用场景
76 0
|
前端开发 JavaScript API
React组件库设计 | 关于我一边写Concis一边给字节组件库arco design提pr的分享
看过我最近的一些文章的小伙伴应该都知道,博主最近半年时间一直在写自己的React组件库Concis,其实这也是从造轮子 -> 学习的目的去做这件事的。
144 1
React组件库设计 | 关于我一边写Concis一边给字节组件库arco design提pr的分享
|
前端开发 API
用 React 构建可复用的设计系统(二)
用 React 构建可复用的设计系统
88 0
|
前端开发 数据处理
【React工作记录五十五】子组件的数据无法实时渲染
【React工作记录五十五】子组件的数据无法实时渲染
94 0
|
前端开发
react实战笔记139:使用RTK构建store1
react实战笔记139:使用RTK构建store1
103 0
react实战笔记139:使用RTK构建store1