简介:
dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验 (dva就是对react进行了语法上面的简化 让我们在react中使用状态管理工具变得更简单),redux-saga 就是redux进行异步操作的一个技术。
1.官网
地址:DvaJS
2.安装与运行
- 全局安装 npm install -g dva-cli
- 测试 dva -v
- cd到指定文件夹之下 dva new 你的项目名
- cd到项目下面 npm install之后再启动
- 启动 npm start
项目目录结构:
3.路由
在dva中使用内置的路由我们可以把路由页面创建到src下的routes文件夹中,当然我们也可以重新创建一个文件夹来容纳路由页面
- 创建路由页面
- 配置路由规则 src文件夹下的router.js文件中来进行编写
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
// 1.引用路由页面
import Home from "./routes/home.jsx"
import Phone from "./routes/phone.jsx"
import User from "./routes/user.jsx"
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
{/* 2.配置路由规则 */}
<Route path="/home" exact component={Home} />
<Route path="/phone" exact component={Phone} />
<Route path="/user" exact component={User} />
</Switch>
</Router>
);
}
export default RouterConfig;
4.路由导航
4.1编程式--js
props.history.push("/去哪里")
注意:使用编程式导航必须在被路由所管理的页面中进行才可以,否则就要使用withRouter这个高阶组件来传递路由跳转的属性。
import React from 'react'
// 引用Link组件来自于dva 的路由
import {Link,withRouter} from "dva/router"
function topbar(props) {
let fun=()=>{
// 编程式导航\
// 在组件中定义了一个编程式导航
// 但是在运行的时候发现push没有定义
// 因为 在使用编程式导航的时候 由于当前组件不是被路由所管理的页面 那么他就不具备路由跳转的功能
// 就会出现push没有定义
// 所以我们就要使用HOC--高阶组件--withRouter来解决
props.history.push("/user")
}
return (
<div>
{/* 声明式导航 */}
<Link to="/">首页</Link>
<Link to="/home">home</Link>
<Link to="/phone">phone</Link>
<Link to="/user">user</Link>
{/* 编程式 */}
<button onClick={fun}>点我去user</button>
</div>
)
}
export default withRouter(topbar)
4.2声明式--标签组件
import React from 'react'
// 引用Link组件来自于dva 的路由
import {Link} from "dva/router"
export default function topbar() {
return (
<div>
{/* 声明式导航 */}
<Link to="/">首页</Link>
<Link to="/home">home</Link>
<Link to="/phone">phone</Link>
<Link to="/user">user</Link>
</div>
)
}
5. model
dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。
(dva中的model 就是用来把状态管理工具进行模块化设置的,为什么要设置模块化?
因为组件多了 数据与修改就变得很多,如果我们把一个项目的所有数据与修改都放在一起,后期维护就很麻烦,所以这个时候我们就可以进行模块拆分)
- 在src的modules文件夹中 创建对应的模块文件
export default {
namespace: 'userm',//命名空间---就是给当前这个模块起个名字(唯一的)
state: {
name:"我是user模块中的数据"
},//存放数据的
subscriptions: {//类似于一个监听 在里面可以绑定很多个监听的方案
},
effects: {//类似于vuex的actions 进行异步触发的
},
reducers: {// 修改上面的state的地方
},
};
- 把我们新建的模块和项目建立关联 在srtc下的index.js中建立
import dva from 'dva';
import './index.css';
// 1. Initialize
const app = dva();
// 2. Plugins
// app.use({});
// 3. Model 就是建立我们模块与项目的位置
// app.model(require('./models/example').default);
app.model(require('./models/homem').default);
app.model(require('./models/phonem').default);
app.model(require('./models/userm').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
6.读取数据
import React from 'react'
import Topbar from "../components/topbar.jsx"
// 1.引用组件和模块的链接工具--connect 是一个方法 当这个方法被调用的时候他才是一个链接的高阶组件
import { connect } from 'dva'
function Home(props) {
return (
<div>
<Topbar></Topbar>
{/* props.state.你要引用的模块的命名空间名字.数据名 */}
<h1>我想读取dva中模块的数据----{props.state.homem.name}</h1>
<h1>我想读取dva中另一个模块的数据----{props.state.userm.name}</h1>
</div>
)
}
// 2.设置数据
export default connect(state=>({state}))(Home)
7.修改数据
- 在组件内通过dispatch来触发修改
import React from 'react'
import Topbar from "../components/topbar.jsx"
import { connect } from 'dva'
function Phone(props) {
let fun=()=>{
// 触发dva的数据修改
// dispatch({type:模块的命名空间名字/你的reducer的名字})
props.dispatch({type:"phonem/UP_NAME"})
}
return (
<div>
<Topbar></Topbar>
phone----{props.state.phonem.name}
<button onClick={fun}>点我修改数据</button>
</div>
)
}
export default connect(state=>({state}))(Phone)
- 编写对应修改的reducers
export default {
namespace: 'phonem',//命名空间---就是给当前这个模块起个名字(唯一的)
state: {
name:"我是phone模块中的数据"
},//存放数据的
subscriptions: {//类似于一个监听 在里面可以绑定很多个监听的方案
},
effects: {//类似于vuex的actions 进行异步触发的
},
reducers: {// 修改上面的state的地方
// state:就是上面的数据源
// payload就是接收dispatch的第二个参数
UP_NAME(state,payload){
return {...state,name:"我吧phone的数据修改了"}
}
},
};
8.effects--异步触发
扩展-Generator
在dva中如果我们要使用effects来进行异步触发 那么我们需要使用es6中新特性之--- Generator Generator 函数的作用:让我们的函数可以根据我们开发者的需求走走停停
- 如果我们想控制函数的执行或者暂停,那么我们需要把函数变成generator函数,就是在function关键字 与函数名之间加一个*号
function *fun(){
console.log(111);
console.log(222);
console.log(333);
}
- 我们需要让函数根据我们的需要走走停停 那么我们就必须在函数中加入表示 来表明暂停的位置是哪里 yield(就是函数暂停的分割线)
function *fun(){
console.log(111);
yield
console.log(222);
yield
console.log(333);
}
- 我们在调用的时候 还需要告诉当前的generator函数 你要执行 next()
function *fun(){
console.log(111);
yield
console.log(222);
yield
console.log(333);
}
// 我们要执行函数中的内容
let funger=fun()
// 想让函数执行
funger.next()
9、effect使用
- 在组件中还是使用dispatch来触发
import Topbar from "../components/topbar.jsx"
import {connect} from "dva"
function User(props) {
let fun=()=>{
// 触发effects进行异步操作
// props.dispatch({type:"你要调用的模块命名空间名字/effect的名字"})
props.dispatch({type:"userm/LINK"})
}
return (
<div>
<Topbar></Topbar>
user
<button onClick={fun}>点我触发异步操作</button>
</div>
)
}
export default connect()(User)
- 编写effects异步触发器
export default {
namespace: 'userm',//命名空间---就是给当前这个模块起个名字(唯一的)
state: {
name:"我是user模块中的数据"
},//存放数据的
subscriptions: {//类似于一个监听 在里面可以绑定很多个监听的方案
},
effects: {//类似于vuex的actions 进行异步触发的 effects的函数必须是一个Generator函数
*LINK(){
console.log("我被触发了");
}
},
reducers: {// 修改上面的state的地方
},
};
- 设置请求
在dva中默认已经帮助我们封装好了异步请求 我们不需要单独去配置与下载 在service文件夹下新建 文件 创建请求
import request from '../utils/request';
export function query() {
return request('http://localhost:8888/userlist/getlink');
}
- 把写好的请求在effects中使用
// 1.引用我们刚才写好的请求
import {query} from "../services/userlink.js"
export default {
namespace: 'userm',
state: {
name:"我是user模块中的数据"
},
subscriptions: {
},
effects: {
// 2.使用异步请求
// call 调用异步操作 并且得到成功的值
*LINK({payload},{put,call}){
console.log("我被触发了");
// 下一步 使用call来获取成功的返回值
let data= yield call(query
}
},
reducers: {
},
};
- 把请求成功的数据复制给state方便组件使用( 先要交给reducers )
// 1.引用我们刚才写好的请求
import {query} from "../services/userlink.js"
export default {
namespace: 'userm',
state: {
name:"我是user模块中的数据"
},
subscriptions: {
},
effects: {
// 2.使用异步请求
// call 调用异步操作 并且得到成功的值
// put把effects中触发reducers
*LINK({payload},{put,call}){
console.log("我被触发了");
// 下一步 使用call来获取成功的返回值
let data= yield call(query
// 把请求来的数据通过reducers修改给state
// 下一步 通过put触发reducers
yield put({type:"UP_NAME",data})
}
},
reducers: {
},
};
- 编写对应的reduicers
// 1.引用我们刚才写好的请求
import {query} from "../services/userlink.js"
export default {
namespace: 'userm',
state: {
name:"我是user模块中的数据"
},
subscriptions: {
},
effects: {
// 2.使用异步请求
// call 调用异步操作 并且得到成功的值
// put把effects中触发reducers
*LINK({payload},{put,call}){
console.log("我被触发了");
// 下一步 使用call来获取成功的返回值
let data= yield call(query
// 把请求来的数据通过reducers修改给state
// 下一步 通过put触发reducers
yield put({type:"UP_NAME",data})
}
},
reducers: {
// 设置修改
UP_NAME(state,payload){
return {...state,name:payload.data}
}
},
};
- 在页面读取数据
import Topbar from "../components/topbar.jsx"
import {connect} from "dva"
function User(props) {
let fun=()=>{
// 触发effects进行异步操作
// props.dispatch({type:"你要调用的模块命名空间名字/effect的名字"})
props.dispatch({type:"userm/LINK"})
}
return (
<div>
<Topbar></Topbar>
user---{props.state.userm.name}
<button onClick={fun}>点我触发异步操作</button>
</div>
)
}
export default connect(state=>({state}))(User)
- 在运行之后出现了跨域问题,项目的根路径下有一个webpackrc的文件中添加跨域
{
"proxy":{
"/api":{
"target":"http://localhost:8888",
"changeOrigin":true,
"pathRewrite":{
"^/api":"/"
}
}
}
}
- 修改请求地址为/api
import request from '../utils/request';
export function query() {
return request('/api/userlist/getlink');
}
- 修改下请求的传递数据
// 1.引用我们刚才写好的请求
import {query} from "../services/userlink.js"
export default {
namespace: 'userm',
state: {
name:"我是user模块中的数据"
},
subscriptions: {
},
effects: {
// 2.使用异步请求
// call 调用异步操作 并且得到成功的值
// put把effects中触发reducers
*LINK({payload},{put,call}){
console.log("我被触发了");
// 下一步 使用call来获取成功的返回值
let data= yield call(query)
console.log(data.data.msg);
// 把请求来的数据通过reducers修改给state
// 下一步 通过put触发reducers
yield put({type:"UP_NAME",data:data.data.msg})
}
},
reducers: {
// 设置修改
UP_NAME(state,payload){
return {...state,name:payload.data}
}
},
};