DvaJs:React数据流解决方案

DvaJs:React数据流解决方案

摘要

本文最终想要达到的目的是掌握DvaJs的使用,从起源开始讲起。文章内容借鉴了阮一峰博客与各技术官网。

Flux篇

Fulx是什么?

Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC架构是同一类东西,但是更加简单和清晰。

为什么要有Flux架构?因为15年Facebook的React火了,但是React有它的缺点,React本身只涉及UI层,搭建大型的前端项目时,只用React就不够用了,必须要有前端框架。不然会出现代码杂乱无法管理,代码重复等问题。
所以Facebook提出了Flux架构,用来管理前端项目结构与项目数据流动。它的具体作用还要看他的实现框架(Flux有很多实现方式,Redux就是其中一种)。

Flux的基本概念

Flux将一个前端应用分为四个部分:

  1. View: 视图层
  2. Action(动作):视图层发出的消息(比如mouseClick)
  3. Dispatcher(派发器):用来接收Actions、执行回调函数
  4. Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux 的最大特点,就是数据的"单向流动"

  1. 用户访问 View
  2. View 发出用户的 Action
  3. Dispatcher 收到 Action,要求 Store 进行相应的更新
  4. Store 更新后,发出一个"change"事件
  5. View 收到"change"事件后,更新页面
    任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰

如图:

图片可见阮一峰博客(http://www.ruanyifeng.com/blog/2016/01/flux.html)

详情见阮一峰博客:http://www.ruanyifeng.com/blog/2016/01/flux.html

Generator

dva基于redux和redux-saga,redux不能解决异步回调的问题,所以出现了redux-sage来解决异步回调,而redux-sage是基于Generator的,所以了解dva之前还需要了解ES6推出的Generator。

介绍

Generator是一种函数,有两个区分它和普通函数的地方

  1. function关键字后面,函数名之前,有个*
  2. 函数内部有关键字yield
function* hello(){
    console.log("first time");
    yield '1';
    console.log("second time");
    yield '2';
    console.log("third time");
    yield '3';
}

直接调用Generator函数,不会被执行,我们需要这样去调用。

let h=hello();
h.next();  //输出‘first time’  {value: "1", done: false}
h.next();  //输出‘second time’  {value: "2", done: false}
h.next();  //输出‘third time’  {value: "3", done: true}
h.next();  //输出{value: undefined, done: true}

Generator每次执行到yield关键字后就会停下,yield相当于函数的return关键字,返回一个IteratorResult对象,该对象包含两个属性{value:string,done:boolean},value就是yield后面的值,done表示函数后面是否还有yield

DvaJs

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

基本介绍

dva是蚂蚁金服团队开发的框架,封装了redux、react等,并没有引入任何新概念。

dva的一些概念

  • state:model的状态数据
  • action:修改state的动作
  • dispatch:发送action的函数,将action发送给state
  • reducer:同步函数放在这里,同步函数可以直接修改state
  • effect:异步函数放在这里,异步函数不能直接修改state
  • Subscription:监听路由,路由发生变化时,可以做一些事情
  • request:请求后端的函数

主要调用流程

页面=>model=>service

  • 页面中使用dispatch调用model,一般是调用model中的effects,也有可能调reducer
  • model中包含reducer、effects、subscription。一般的调用顺序是effect调用service中的函数,获取到数据,然后调用reducer中的函数,把获取的数据传给reducer,由reducer去修改state.
  • reducer因为要直接修改state,所以它一定要返回数据
  • 页面与model没有直接的关联,要在connect中连接起来
    页面中使用dispatch
/**
 * 连接model与组件后,将model中的数据传给组件
 * 组件中可以使用this.props获取
 * @param state reduce中的函数返回的state
 * @returns 返回一个对象,该对象中的值,可以直接在上面的组件中使用。(this.props)
 */
const mapStateToProps = (state) => {
  console.log(state);
  return {
    ...state,
  }
}


//这种连接方式是redux的连接方式,好像可以使用注解的方式,待了解。
export default connect(mapStateToProps)(Index);

model中定义reducer effect

import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
import { getRemoteList} from './service';


const IndexModel: IndexModelType = {
  namespace: 'users',
  state: {
    tableData: [],
    result:{}
  },

  effects: {  
  //effects中的函数的参数为(action:{payload},effects:{put,call}),有时会简写为({payload},{put,call})
    *getTest(action, effects) {
      const serviceData=yield effects.call(getRemoteList,{currentPage:1,pageSize:5})
      yield effects.put({
        type:'get',
        payload:{
          tableData:data,
          result:serviceData,
        },
      })
    },  
  },
  reducers: {
  //state是当前状态数据,action包含两个属性{type,payload},type几乎不用,所有有时会写为get(state,{payload})
    get(state, {payload}) {  
      let _state={...state};
      // _state=action.payload;
      //返回的数据比较讲究,必须包含state中的数据,如果不包含所有,也可以,只是ts会报错
      return {
        ...payload,
      };
    },
    // 启用 immer 之后
    // save(state, action) {
    //   state.name = action.payload;
    // },
  },
  subscriptions: {
    setup({ dispatch, history }) {
      return history.listen(({ pathname }) => {
        if (pathname === '/') {
          dispatch({
            type: 'query',
          });
        }
      });
    },
  },
};

export default IndexModel;

service中定义request,request请求后端

umi内部提供两种方式请求后端umi-request和request
这里使用request(因为umi-request更高深) request文档见这里

请求后端都是异步函数,定义异步函数的两种方式:

  • async function function_name(){}
  • const name = async ()=>{}
import request, { extend } from 'umi-request';
import { message } from 'antd';
import BASE_API from '@/myConfig/base_api';

export const getRemoteList = async ({
    currentPage,
    pageSize,
  }) => {
    return request(
      `${BASE_API}/checkInfo/6/0/${pageSize}/${pageSize}`,
      {
        method: 'get',
      },
    )
      .then(function(response) {
        return response;
      })
      .catch(function(error) {
        return false;
      });
  };

这是基本的reuest,下面是加了异常处理的request,再把这个异常处理重构出去,就成了统一异常管理

import request, { extend } from 'umi-request';
import { message } from 'antd';
import BASE_API from '@/myConfig/base_api';

const errorHandler = function(error: any) {
    if (error.response) {
      if (error.response.status > 400) {
        message.error(error.data.message ? error.data.message : error.data);
      }
    } else {
      //请求已经发送但是没有回应
      message.error('网络不佳,请稍后再试!');
    }
  
    throw error; // If throw. The error will continue to be thrown.这里可以看一下
  
    // return {some: 'data'}; If return, return the value as a return. If you don't write it is equivalent to return undefined, you can judge whether the response has a value when processing the result.
    // return {some: 'data'};
  };

const extendRequest = extend({ errorHandler });
export const getRemoteList = async ({
    currentPage,
    pageSize,
  }: {
    currentPage: number;
    pageSize: number;
  }) => {
    return extendRequest(
      `${BASE_API}/checkInfo/6/0/${pageSize}/${pageSize}`,
      {
        method: 'get',
      },
    )
      .then(function(response) {
          console.log("成功");
        return response;
      })
      .catch(function(error) {
        console.log("失败");
        return false;
      });
  };

其他

tsx、jsx、ts文件的区别

  • 使用了TypeScript的js文件就叫ts文件
  • 使用了Jsx语法的js文件就叫jsx文件
  • 使用了TypeScript和Jsx语法的js文件就叫tsx文件

函数参数

dva中很多函数的参数都是一个对象,对象中有多个属性,所以有多种写法:

function(A,B)  //B对象中包含c属性和d属性
function(A,{c,d})
function(A,{d})
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值