React系列之DVA

813 阅读8分钟

简介:

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验 (dva就是对react进行了语法上面的简化 让我们在react中使用状态管理工具变得更简单),redux-saga 就是redux进行异步操作的一个技术。

1.官网

地址:DvaJS

2.安装与运行

  1. 全局安装 npm install -g dva-cli
  2. 测试 dva -v
  3. cd到指定文件夹之下 dva new 你的项目名
  4. cd到项目下面 npm install之后再启动
  5. 启动 npm start

项目目录结构:

企业微信截图_17364725789067.png

3.路由

在dva中使用内置的路由我们可以把路由页面创建到src下的routes文件夹中,当然我们也可以重新创建一个文件夹来容纳路由页面

  1. 创建路由页面
  2. 配置路由规则 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 就是用来把状态管理工具进行模块化设置的,为什么要设置模块化?

因为组件多了 数据与修改就变得很多,如果我们把一个项目的所有数据与修改都放在一起,后期维护就很麻烦,所以这个时候我们就可以进行模块拆分)

  1. 在src的modules文件夹中 创建对应的模块文件
export default {
    namespace: 'userm',//命名空间---就是给当前这个模块起个名字(唯一的)
    state: {
        name:"我是user模块中的数据"
    },//存放数据的
    subscriptions: {//类似于一个监听  在里面可以绑定很多个监听的方案
    },
    effects: {//类似于vuex的actions  进行异步触发的
    },
    reducers: {//  修改上面的state的地方
    },
  };
  1. 把我们新建的模块和项目建立关联 在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.修改数据

  1. 在组件内通过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)
  1. 编写对应修改的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 函数的作用:让我们的函数可以根据我们开发者的需求走走停停

  1. 如果我们想控制函数的执行或者暂停,那么我们需要把函数变成generator函数,就是在function关键字 与函数名之间加一个*号
function *fun(){  
    console.log(111);  
    console.log(222);  
    console.log(333);  
}
  1. 我们需要让函数根据我们的需要走走停停 那么我们就必须在函数中加入表示 来表明暂停的位置是哪里 yield(就是函数暂停的分割线)
function *fun(){
    console.log(111);
    yield
    console.log(222);
    yield
    console.log(333);
}
  1. 我们在调用的时候 还需要告诉当前的generator函数 你要执行 next()
 function *fun(){
    console.log(111);
    yield
    console.log(222);
    yield
    console.log(333);
}
// 我们要执行函数中的内容
let funger=fun()
// 想让函数执行
funger.next()

9、effect使用

  1. 在组件中还是使用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)
  1. 编写effects异步触发器
export default {
    namespace: 'userm',//命名空间---就是给当前这个模块起个名字(唯一的)
    state: {
        name:"我是user模块中的数据"
    },//存放数据的
    subscriptions: {//类似于一个监听  在里面可以绑定很多个监听的方案
    
    },
    effects: {//类似于vuex的actions  进行异步触发的  effects的函数必须是一个Generator函数
        *LINK(){
            console.log("我被触发了");
        }
    },
    reducers: {//  修改上面的state的地方
     
    },
  };
  1. 设置请求

在dva中默认已经帮助我们封装好了异步请求 我们不需要单独去配置与下载 在service文件夹下新建 文件 创建请求

import request from '../utils/request';

export function query() {  
  return request('http://localhost:8888/userlist/getlink');  
}
  1. 把写好的请求在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: {
     
    },
  };
  1. 把请求成功的数据复制给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: {
     
    },
  };
  1. 编写对应的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}
        }
    },
  };
  1. 在页面读取数据
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)
  1. 在运行之后出现了跨域问题,项目的根路径下有一个webpackrc的文件中添加跨域
{
    "proxy":{
        "/api":{
             "target":"http://localhost:8888",
             "changeOrigin":true,
             "pathRewrite":{
               "^/api":"/"
             }
        }
    }
}
  1. 修改请求地址为/api
import request from '../utils/request';

export function query() {  
  return request('/api/userlist/getlink');  
}
  1. 修改下请求的传递数据
// 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}
        }
    },
  };

ubscriptions 设置一个监听