dva框架-快速了解

2022-07-22 14:54:27 浏览数 (1)

0 1

dva的介绍

官方文档:

https://dvajs.com/guide/

背景:

使用redux-saga需要在action , reducers,saga三个文件中切换,而可以使用dva框架来简化这一个过程.

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

0 2

dva的安装

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

安装dva

代码语言:javascript复制
npm install dva-cli -g
dva -v
dva-cli version 0.9.1

创建项目

代码语言:javascript复制
dva new dva-quick
cd dva-quickstart
npm start

0 3

dva的目录结构

0 4

models文件介绍

model模块的写法:

namespace: 命名空间,后续我们取得数据的时候要注意加上命名空间

state: 状态,即数据

subscriptions:这里的方法,直接就会执行,一般用于数据的初始化

reducers: 纯函数,如果有异步,必须借助effect

effects:副作用(业务逻辑,包含同步和异步)

这里的键名,就是action中type的字段

payload: action传参数,都放在这里

call() : 表示调用异步函数

如果yield call的是一个Promise对象,那只有在Promise返回的是resolve方法的情况下,下面跟着的yield put及后面的代码才会执行,若返回了rejector则后面的代码则全部停止执行

put 表示 dispatch action,其他的还有 select, take, fork, cancel 等

注:yield put直接调用reducer,是堵塞的,同步,

调用非reducer函数,是非堵塞的,异步

使用put.resolve ,堵塞的,同步

model模块的写法,如下:

代码语言:javascript复制
import { getTodolistDataApi } from '../services/todolist'
export default{
    namespace:"todolist",//命名空间,后续我们取得数据的时候要注意加上命名空间 , 把它和combineReducer 接受参数的键名关联起来
    // 数据
    state:{
        tasks:[]
    },
          // subscriptions里的方法,直接就会执行,一般用于数据的初始化
    subscriptions:{ 
  
      setup({dispatch,history}){ //可以是任何键名
        // 另外,参数会自动注入进来,有dispatch,history
          console.log('init');
           dispatch({type:'common/setToken',payload:'123'})
      }
    },
    // 副作用(一些业务逻辑,包含同步和异步,你可以把它和vuex里面的mutations 和actions关联起来)
    effects:{
        /*这里的键名,就是action中type的字段*/
        *getData({payload},{call,put}){
           const res =  yield call(getTodolistDataApi);
           console.log(res.data.data);
           yield put({type:'setTasks',payload:res.data.data})
        }
    },
    reducers:{
        setTasks(state,action){
             return {...state,tasks:action.payload}
        }
    }
}

0 5

services 请求数据

将请求后台数据的方法,全部提取到services文件夹中

代码如下:

代码语言:javascript复制
import request from "../utils/request";
// 获取购物车数据 
export const getShopcartDataApi = ()=> request('/shopcart/getShopcartData')
export const changeNumApi =
 (params)=> request('/shopcart/changeNum',{
     method:"post",
     body:params
})

0 6

util 公共文件

将提取的一些公共方法放在此文件夹中

如:封装的请求方法request.js:

代码语言:javascript复制
import fetch from 'dva/fetch';

const baseUrl = 'http://172.16.102.181';
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;
}
/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"
 */
export default function request(url, options={method:'get'}) {
  if(options.method.toLowerCase()=='post'){
    options.body = JSON.stringify(options.body || {});
    options.headers={
      'Content-Type':'application/json;charset=utf-8'
    }
  }
  return fetch(baseUrl url, options)
    .then(checkStatus)
    .then(parseJSON)
    .then(data => ({ data }))
    .catch(err => ({ err }));
}

0 7

router.js 路由设置文件

与react-router-dom v5 语法相似,只是已经将其它封装到dva中,所以要从dva中去引用router.

代码如下:

代码语言:javascript复制
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import Home from './routes/home';
import Login from './routes/login';
import Reg from './routes/reg';


function RouterConfig({ history }) {
  return (
    <React.Suspense fallback={<div>加载中...</div>}>
    <Router history={history}>
      <Switch>
       <Route path="/" exact component={IndexPage} />
       <Route path="/home" component={React.lazy(()=>import('./routes/home'))} />
       <Route path="/login" component={React.lazy(()=>import('./routes/login'))} />
       <Route path="/reg" component={React.lazy(()=>import('./routes/reg'))} />
      </Switch>
    </Router>
    </React.Suspense>
  );
}
export default RouterConfig;

0 8

组件中的路由跳转

NavLink, Route,Redirect等,与react中的react-router-dom v5 语法一致

注: 最新的react-router-dom v6的语法与v5还是有很大的不同

代码语言:javascript复制
import React from 'react'
import { Switch,Route,Redirect,NavLink } from 'dva/router'
import styles from  './index.less'
import {app} from '../../index';
import dynamic from 'dva/dynamic';
const shopcartComponent = dynamic({
  app,
  models: () => [
    import('../../models/shopcart'),
  ],
  component: () => import('../shopcart'),
});
const todolistComponent = dynamic({
    app,
    models: () => [
      import('../../models/todolist'),
    ],
    component: () => import('../todolist'),
  });
export default function Index(){
    return (
    <div className={styles.home}>
        <div className={styles.left}>
           <div>
           <NavLink to='/home/person' activeStyle={{color:'red'}}>人员管理</NavLink>
           </div> 
           <div>
           <NavLink to='/home/goods' activeStyle={{color:'red'}}> 商品管理</NavLink>
           </div> 
           <div>
           <NavLink to='/home/shopcart' activeStyle={{color:'red'}}> 购物车</NavLink>
           </div> 
           <div>
           <NavLink to='/home/todolist' activeStyle={{color:'red'}}> 任务列表</NavLink>
           </div>
        </div>
        <div className={styles.right}>
           <Switch>
               <Redirect from='/home' to='/home/person' exact />
               <Route path='/home/person' component={React.lazy(()=>import('../person'))}/>
               <Route path='/home/goods' component={React.lazy(()=>import('../goods'))}/>
               <Route path='/home/shopcart' component={shopcartComponent}/>
               <Route path='/home/todolist' component={todolistComponent}/>
           </Switch>
        </div>
    </div>)
}

0 9

index.js 入口文件

model():注入数据,需要哪一个models中的数据 ,就必须在入口中对其进行注入,可以与之的Provider关联起来

router(): 引用设置路由的文件

代码语言:javascript复制
import dva from 'dva';
import createLogger from 'redux-logger'
import './index.css';
// 1. Initialize
export const app = dva({
    onAction:createLogger
});

// 2. Plugins
// app.use({});
// 3. Model 数据注入 你可以把它和以前的Provider 组件的功能关联起来
//app.model(require('./models/shopcart').default);
//app.model(require('./models/todolist').default);
app.model(require('./models/common').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');

0 9

routes文件夹,类似pages

和react 组件的写法一样,对于model中的状态使用,也使用connect方法,与redux的使用方法相似

代码如下:

代码语言:javascript复制
import React , {Component} from 'react'
import styles from './index.less';
import {connect} from  'dva';
class Todolist extends Component{
    componentDidMount(){
          this.props.dispatch({type:'todolist/getData'});
         // todolist是Models文件中设置的namespace的值
    }
    render(){
        const {tasks} = this.props; 
        return (
            <>
            <div>
                <input placeholder='请输入任务' /> <button>添加任务</button>
            </div>
            <ul>
                {
                    tasks.map((item)=>{
                       return (<li key={item._id} className={styles[item.status]}>{item.task}</li>)
                    })
                }
              
                <li className={styles.undone}>学习react</li>
            </ul>
            <div>
                <button className={styles.active}>全部</button>
                <button>已完成</button>
                <button>末完成</button>
            </div>
            </>
        )
    }
}
const mapStateToProps = (state)=>{
    return {
        tasks : state.todolist.tasks
    }
}
export default connect(mapStateToProps)(Todolist)

总结

dva框架介绍完成,希望通过本文能让大家对dva框架有个基本了解

0 人点赞