浅说dva

什么是dva

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

快速上手

通过 npm 安装 dva-cli 并确保版本是 0.9.1 或以上。

$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.1

创建新应用

$ dva new dva-quickstart

然后我们cd到项目目录,运行

$ cd dva-quickstart
$ npm start

这时我们便创建并运行起来了。

引入antd

使用npm安装antd

$ npm install antd babel-plugin-import --save

编辑 .webpackrc,使 babel-plugin-import 插件生效。

{
+  "extraBabelPlugins": [
+    ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
+  ]
}

到此,我们的准备工作完成😊

定义路由

打开src目录,我们可以看到routes的目录,这里放置着我们需要挂载到router上的页面(page),这里dva已经给我们创建了一个叫做IndexPage的页面,我们再创建一个新的页面(ProductPage),然后再打开src下的router文件,在这里我们可以配置所有的挂载到路由上的页面。
在这里插入图片描述
在这里插入图片描述

创建新的Component组件

我们在src下的components文件夹下新建一个Products的jsx文件,然后写一个Products的函数式组件(类式组件也行,在这里使用了函数式组件)。

import React from "react";

function Products(){
    return (
        <div>products</div>
    )
}

export default Products;

然后在之前的ProductsPage页面中引入。

import Products from '../../components/Products';

const ProductsPage = () => {
  return (
    <div>
      <Products/>
    </div>
  );
};

// export default Products;
export default ProductsPage;

Model创建

在src下,我们可以看见一个models的文件夹,在这里面我们新建一个products.js文件。
照着给的示例,我们在products中写一些东西。

export default {
    namespace: 'products',
    state: {
      productsList:[
        {name:'大豆'},
        {name:'小米'}
      ]
    },
  };

namespace:命名空间,标识我们这个model的名字。state存储数据。当然这里的state的初始话数据我们还可以放在src下的index文件中。
在这里插入图片描述
接下来,我们继续完善一下model

    reducers:{
      updateList(state, action){ //此处的state不和model中的state严格使用一样的名字
        let curentList = deepClone(state);
        curentList.productsList.push(action.payload); //此处的payload和dispatch中传入的相对应,名字一样
        return curentList;
      }
    },
    
  };

   function deepClone(value){
    return JSON.parse(JSON.stringify(value));
  }

再在ProductsPage中连接model


import Products from '../../components/Products';
import { connect } from 'dva';

const ProductsPage = (props) => {
  console.log(props);
  return (
    <div>
      <Products dispatch={props.dispatch} productsList={props.productsList}/>
    </div>
  );
};

const mapStateToProps = (state)=>{
  return {
    productsList:state.products //products是命名空间的名字
  }
}

// export default Products;
export default connect(mapStateToProps)(ProductsPage);

connect是dva提供的用来连接model的高阶函数。
上面ProductsPage中向组件Products传了参数,在Products子组件中接收。

import React from "react";

function Products(props){
    const {productsList} = props.productsList;
    const addProducts=()=>{
        const curProduct = {name:'玉米'};
        props.dispatch({
            type:'products/updateList', //此处为model中的namespace+reducers中的方法的名字
            payload:curProduct //此处的payload和model中的相对应
        })
    }
    return (
        <div>products
            <ul>
                {productsList.map((item,index)=>{
                    return <li key={index}>{item.name}</li>
                })}
            </ul>
             <button onClick={addProducts}>添加商品</button>
        </div>
    )
}

export default Products;

路由跳转

  1. Link事件跳转
import {Link} from "dva/router"
//.......

<Link to="/">去首页</Link>
  1. 点击事件
    利用props上的history(只有被Router包裹的才有history属性)
<Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
        <Route path="/products" component={ProductsPage}/>
      </Switch>
    </Router>

所以在这里我们的子组件Products没有history属性,这时我们就需要从父组件中传参到子组件。

//父组件传递
<Products history={props.history} dispatch={props.dispatch} productsList={props.productsList}/> 
//子组件接收
const gotoHome = ()=>{
        props.history.push('/');
    }
//......
<button onClick={gotoHome}>去首页</button>

利用原生路由提供的withRouter(dva把原生的代码直接拷过来了,dva中也有此提供)。
withRouter是一个高阶函数

import {withRouter,Link} from "dva/router"
const gotoHome = ()=>{
        props.history.push('/');
    }
<button onClick={gotoHome}>去首页</button>
export default withRouter(Products);
  1. routerRedux方案
import {withRouter,Link,routerRedux} from "dva/router"

const gotoHomeRedux = ()=>{
        props.dispatch(routerRedux.push('/'))
    }
<button onClick={gotoHomeRedux}>去首页Redux</button>

model异步处理

比起model的同步操作,异步操作是在effects中进行处理之后再传递一个action到reducers中请求相关操作完成state的更新。下面让让我们来看一个简单的例子

const addProductsAnysc = ()=>{
        const curProductAnysc = {name:'异步玉米'}
        props.dispatch({
            type:'products/updateListAnysc',
            payload:curProductAnysc
        })
    }
<button onClick={addProductsAnysc}>异步添加商品</button>

//---------------------------------------------------------------

reducers:{
      updateList(state, action){
        console.log('state',state);
        let curentList = deepClone(state);
        curentList.productsList.push(action.payload);
        return curentList;
      }
    },
    effects:{
      *updateListAnysc({payload},{call,put}){
        yield put({
          type:'updateList',
          payload
        })
      }
    }

如上展示的代码,我们先在子组件中创建一个点击的按钮用来触发事件,该写法和原来没有什么区别,唯一改变的是调用的effects中的方法,再通过effects中的方法调用了reducers中的方法更新state。


关于dva中的mock使用

  1. 在根目录下的mock文件中新建一个product.js文件
module.exports = {
    "GET /api/product":{"name":"高粱"}
}
  1. 在根目录下的.roadhogrc.mock.js中添加
 export default {
     ...require("./mock/product.js")
 };
  1. 最后在src下的service中的example文件中添加如下
export function getProduct(){
  return request('/api/product')
}

可以更改下src中utils下的request.js文件(新增了post)

import fetch from 'dva/fetch';
import qs from 'querystring';

function parseJSON(response) {
  return response.json();
}

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  const error = new Error(response.statusText);
  error.response = response;
  throw error;
}

function handleHeaders(options) {
  const headers = options.headers = options.headers ? options.headers : {};
  const defaultHeaders = {
    'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
  };
  options.headers = Object.assign({}, defaultHeaders, headers);

  if (options.method === 'post') {
    var body = options.body ? options.body : {};
    body = qs.stringify(body);
    options.body = body;
  }
}


/**
 1. Requests a URL, returning a promise.
 2.  3. @param  {string} url       The URL we want to request
 4. @param  {object} [options] The options we want to pass to "fetch"
 5. @return {object}           An object containing either "data" or "err"
 */
export default function request(url, options = {}) {

  //get
  if (!options.method) {
    url += `?${qs.stringify(options.params)}`;
  }
  //处理头部
  handleHeaders(options);

  return fetch(url, options)
    .then(checkStatus)
    .then(parseJSON)
    .then(data => ({ data }))
    .catch(err => ({ err }));
}

上面这些是我们自己自定义的数据,如果我们想使用mock提供的数据呢?

  1. 安装mock npm install mockjs
  2. 在mock文件夹下新增posts文件
const Mock = require("mockjs")
module.exports = {
    'GET /api/posts': (req, res) => {
        res.status(200).json({
            users: Mock.mock({
                'data|80-100': [
                    {
                        id: '@id',
                        name: '@name',
                        nickName: '@last',
                        phone: /^1[34578]\d{9}$/,
                        'age|11-99': 1,
                        address: '@county(true)',
                        isMale: '@boolean',
                        email: '@email',
                        createTime: '@datetime',
                        avatar() {
                            return Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', this.nickName.substr(0, 1))
                        },
                    },
                ],
            })
        })
    },

}
  1. 配置service
export function posts(){
  return request('/api/posts');
}

dva中调用mock提供的数据

先是子组件中

const addProductsHttp = ()=>{
        props.dispatch({
            type:'products/updateListHttp',
            payload:101
        })
    }
<button onClick={addProductsHttp}>添加高粱</button>

再是model里面

reducers:{
      updateList(state, action){
        console.log('state',state);
        let curentList = deepClone(state);
        curentList.productsList.push(action.payload);
        return curentList;
      }
    },
    effects:{
      *updateListAnysc({payload},{call,put}){
        yield put({
          type:'updateList',
          payload
        })
      },
      *updateListHttp({payload}, {call,put}){
        const result = yield call(api.getProduct,payload)
        console.log('payload',payload);
        const data = result.data;
        if(data){
          yield put({
            type:'updateList',
            payload:data
          })
        }
      }
    }

结语:在这里也只是很浅显的学习了一下dva,参考了b站老师的视频https://www.bilibili.com/video/BV1R441137as?p=1 如果文章有不对的地方,欢迎大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值