Dva 直接秒杀😏

401 阅读5分钟

dva介绍

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

Dva设计的目的就是简化元素,降低难度,让你不用管他怎么实现的,我们按照默认的这个规则去写就可以,大致就相当于 它写好了规则必须按照规则来。

Dva是一个约定式的model组织方式 符合以下规则的文件会被认为是 model 文件,

  • src/models 下的文件
  • src/pages 下,子目录中 models 目录下的文件
  • src/pages 下,所有 model.ts 文件(不区分任何字母大小写)

dva是umi内置的一款插件,主要用来管理数据流

reduxdva
状态数据statestate
行为描述actionaction
无副作用业务reducerreducer
有副作用业务creatorseffect
通讯请求修改状态函数dispatchdispatch
通讯请求获取状态函数connextconnect
从源获取subscription

数据流向

image.png

数据的改变一般是由用户或者浏览器的一些交互行为,当这些行为会去改变数据时候, 组件通过dispatch的请求,这个请求是一个对象(action),如果这个action是同步的会通过reducer直接改变state,如果是异步的或者带有副作用的effect然后流向reducer最终改变state,state会通过connect函数流回组件

分层开发

主要的分层结构:

  • Page 负责与用户直接打交道:渲染页面,接受用户的操作输入,侧重于展示型交互性逻辑,这里需要了解无状态组件
  • Model 负责处理业务逻辑,为 Page 做数据、状态的读写、变换、暂存等,Dva中model就是做了这一层的操作
  • Service 负责与 HTTP 接口对接,进行纯粹的数据读写

基础概念

  1. namespace
    • model的命名空间,同时也是他在全局state上的属性
    • 只能用字符串,不支持通过.的方式创建多层命名空间,相当于这个modelkey
    • 在组件里面,通过connect这个key将想要引入的model加入
import { connect } from 'dva'; 
export default connect(({ namespaceValue }) => ({ ...namespaceValue }))(DvaCompoent);
  1. state

    • 表示model的状态数据
    • 操作的时候每次都要当作不可变数据immutable data来对待,保证每次都是全新对象,没有引用关系
  2. reducer

    • 必须是纯函数,有固定输入输出,主要目的是修改自身state
    • 接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果,该函数把一个集合归并成一个单值
    • 需要注意的是同样的输入必然得到同样的输出,它们不应该产生任何副作用effect。并且,每一次的计算都应该使用immutable data
  3. effect

    • 主要用于异步请求,接口调用之类的
    • effect被称为副作用,在我们的应用中,最常见的就是异步操作
    • 它来自于函数编程的概念,之所以叫副作用是因为它使得我们的函数变得不纯,同样的输入不一定获得同样的输出
  4. subscription

    • subscription语义是订阅,用于订阅一个数据源,然后根据条件dispatch需要的action
    • 数据源可以是当前的时间、服务器的websocket连接、keyboard输入、geolocation变化、history路由变化等等
    • 内部定义的函数都会被被执行,执行之后作为监听来处理事务
  5. 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);

1.Reducer

    putOrderIncomeFee(state, action) {
      return { ...state, ...action.payload };
    },

用来处理同步操作。如果不需要调接口时候,我们前台传递的 action可以直接调用 reducers里的方法。

reducer是一个函数,接收state 和 action,返回老的或新的state。

state:为当前 Model 下的所有 state 值,可以 console.log(state) 看一下输出就知道了。 action:当前台页面需要进行数据操作时,就会创建一个 action,action 存放了传递过来需要对当前 state 进行改变的数据。

2.Effect

用来处理异步操作。如果需要调取接口的话,前台页面就需要调用 effects 里的方法。内部使用 yield 关键字,标识每一步的操作

每一个 effect 都可以接收两个参数:

  1. 包含 dispatch 携带参数 payload 的 action 对象
  2. dva 提供的 effect 函数内部的处理函数集

第二个参数提供的处理函数中,常用的有 call、put、select

  • call: 执行异步函数(与后台服务端接口进行交互)
    • 第一个传参:后台服务器接口对应的名称。
    • 第二个参数:入参。
    • 请求成功之后,调用 reducer 更新 state
  • put: 发出一个 Action,类似于dispatch 触发reducer改变state
    • 用来发出事件,即 action。一般调用 reducers 下的方法进行同步数据。
    • type:该 Model 层里 reducers 下的方法名。
    • payload:参数的传递。
  • select: 返回 model 中的 state //用于从state里获取数据
    • const m = yield select((state) => state.test.num)

3.subscriptions

订阅监听 比如:比如我们监听路由,进入页面就如何,可以在这写

    setup ({ dispatch, history, query }) {
   return history.listen(
   async ({ pathname, search, query}) => {
      if (pathname==="/testdemo") 
      {
          // 当进入testdemo这路由,就会触发fetchUser方法
      dispatch({ type: "fetchUser" })
      }
    })
   }

注意

  • yield call:执行异步请求,阻塞流程

  • yield put:执行effects中的异步方法时,不阻塞流程,和组件中使用dispatch执行effects方法一样;但在执行reducer中的状态更新方法时,会阻塞流程