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框架有个基本了解