Bootstrap

【DvaJS】DvaJS简介和与其他框架之间的差异

什么是DvaJS

DvaJS是一个基于reduxredux-saga的数据流方案,然后为了简化开发体验,dva 还额外内置了react-routerfetch,是一个轻量级的前端应用框架。

何时使用

一般不是过于复杂的组件通讯,都不建议使用dva,一般来说使用函数式组件,通过父子组件传值即可,当业务层面删除线组件间通信多,业务复杂,需要引入状态管理再使用Dva会更好,避免长此以往项目的技术层面的臃肿与冗余。

怎样使用

使用dva version 2.4.1新建一个项目的结构如图1,assets为存放静态资源的地方,components为定义公用组件的文件,models为数据源定义的文件,routes为页面存放的文件,services为定义接口通讯的文件,utils为定义公用方法的文件。
index为入口文件,内容如图代码块1,是dva实例化和引入插件,引入models,路由,和挂载节点的地方,其中要注意的点有以下几点
1.Dva的实例是可以有多个的。
2.实例化Dva时可以传入一个options用于控制实例Dva的属性,例如该实例的路由方式,通过对象中的history属性传入不同的值即可控制,又或是初始的数据,通过initialState给入到对应的model中,注意通过initialState传入初始值会覆盖model原本的state值,等一系列初始化的操作都是在这里完成的。
3.使用插件,引入对应的插件然后use即可。
4.引入需要的数据源。
5.引入对应的路由。
6.挂载到对应的节点
image.png
图一

import dva from 'dva';
import createLoading from 'dva-loading';
import {createBrowserHistory as createHistory} from 'history'; 
import './index.css';


// 1. Initialize
//dva(options)
const options = {
  history, // 指定给路由用的 history,默认是 hashHistory
  initialState:{
  	history:createHistory(),
    //key值为model文件的namespace
    example:{
      initFlag:1
    }
  },  // 指定初始数据,优先级高于 model 中的 state
  onError, // effect 执行错误或 subscription 通过 done 主动抛错时触发,可用于管理全局出错状态。
	...
}
const app = dva(options);


// 2. Plugins
app.use(createLoading());


// 3. Model
app.model(require('./models/example').default);


// 4. Router
app.router(require('./router').default);


// 5. Start
app.start('#root');

代码块1

初始化完了Dva后,我们进行一个业务功能的开发。过程为1.定义路由,2.编写UI组件,3.定义Model数据源,4.将UI组件和数据源连接起来,这样一个使用Dva编写一个业务组件的过程就完成了,让我们来看看每一步需要注意到的细节。

1.定义路由,如代码块2

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
      </Switch>
    </Router>
  );
}

代码块2

2.编写 UI Component,如代码块3,其中mapStateToProps将model中的state数据源传入UI组件中,mapDispatchToProps是定义各种操作model中的state的方法然后作为props传入UI组件中。

import React, { useEffect } from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';

function IndexPage(props) {
  return (
    <span>这是一个Dva示例</span>
  );
}

export default connect(mapStateToProps,mapDispatchToProps)(IndexPage);
//此方法是将model中的state数据源传入UI组件中
function mapStateToProps (state){
  return {
    ...state
  }
}
function mapDispatchToProps(dispatch){
  return {
     updateState:(payload)=>{
      return dispatch({
        type:'example/updateState',
        payload
      })
     }
  }
}

代码块3

3.定义 Model

export default {

  namespace: 'example',

  state: {
    personList:[]
  },
	effects:{
    *fetchIndexListData(payload,{put,call})=>{
      const res = yield call(xxxserver,payload)
      yield put({type:'updateState',payload:res})
    }
  }
  reducers: {
    updateState(state, action) {
      return { ...state, ...action.payload };
    },
  },
  subscriptions:{
    setup({history,dispatch}){
      return history.listen({pathname}) =>{
        if(pathname==='/'){
          dispatch({type:'fetchIndexListData'})
        }
      }
    }
  }
};

4.connect
将UI Component和model连接起来
其中mapStateToProps的作用是将model中的state作为props传给UI Component,mapDispatchToProps的作用是提供一个可以定义操纵model中的state的方法的地方

export default connect(mapStateToProps,mapDispatchToProps)(IndexPage);
function mapStateToProps (state){
  return {
    state
  }
}
function mapDispatchToProps(dispatch){
  return {
     add:(payload)=>{
      return dispatch({
        type:'example/save',
        payload
      })
     }
  }
}

Dva 核心概念

数据流方案

dva数据流方案是基于 Redux 理念的数据流向,数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State,所以在 dva 中,数据流向非常清晰简明。

  • 基于 Redux 的基本概念。包括:
    • State 数据,通常为一个 JavaScript 对象,操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。
    • Action 行为,一个普通 JavaScript 对象,它是改变 State 的唯一途径。
    • dispatch,一个用于触发 action 改变 State 的函数。
    • Reducer 描述如何改变数据的纯函数,接受两个参数:已有结果和 action 传入的数据,通过运算得到新的 state。
    • Effects(Side Effects) 副作用,常见的表现为异步操作。dva 为了控制副作用的操作,底层引入了redux-sagas做异步流程控制。
    • Connect 一个函数,绑定 State 到 View

Dva数据流的过程

  1. 代理 router 和 start 方法,实例化 app 对象
  2. 调用 dva-core 的 start 方法,同时渲染视图
  3. 使用 react-redux 完成了 react 到 redux 的连接。

Dva 与 React、React-Redux、Redux-Saga 之间的差异

redux

  1. 状态及页面逻辑从 里面抽取出来, 成为独立的 store, 页面逻辑就是 reducer
  2. 及都是 Pure Component, 通过 connect 方法可以很方便地给它俩加一层 wrapper 从而建立起与 store 的联系: 可以通过 dispatch 向 store 注入 action, 促使 store 的状态进行变化, 同时又订阅了 store 的状态变化, 一旦状态变化, 被 connect 的组件也随之刷新
  3. 使用 dispatch 往 store 发送 action 的这个过程是可以被拦截的, 自然而然地就可以在这里增加各种 Middleware, 实现各种自定义功能, eg: logging

这样一来, 各个部分各司其职, 耦合度更低, 复用度更高, 扩展性更好。

saga
因为我们可以使用 Middleware 拦截 action, 这样一来异步的网络操作也就很方便了, 做成一个 Middleware 就行了, 这里使用 redux-saga 这个类库, 举个栗子:

  1. 点击创建 Todo 的按钮, 发起一个 type == addTodo 的 action
  2. saga 拦截这个 action, 发起 http 请求, 如果请求成功, 则继续向 reducer 发一个 type == addTodoSucc 的 action, 提示创建成功, 反之则发送 type == addTodoFail 的 action 即可

Dva

  1. 把 store 及 saga 统一为一个 model 的概念, 写在一个 js 文件里面
  2. 增加了一个 Subscriptions, 用于收集其他来源的 action, 比如键盘操作等
  3. model 写法很简约, 类似于 DSL(领域特定语言),可以提升编程的沉浸感,进而提升效率
;