# react-dva-study
**Repository Path**: goodjiang/react-dva-study
## Basic Information
- **Project Name**: react-dva-study
- **Description**: 学习基于react的dva框架
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-04-06
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# react-dva-study
#### 介绍
学习基于react的dva框架
### 1.安装
**1.1 安装 dva-cli**
$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.1
**1.2 创建新应用**
```
dva new dva-quickstart
```
这会创建 dva-quickstart 目录,包含项目初始化目录和文件,并提供开发服务器、构建脚本、数据 mock 服务、代理服务器等功能。
```
$ cd dva-quickstart
$ npm start
```
### 2.使用antd
通过 npm 安装 antd 和 babel-plugin-import 。babel-plugin-import 是用来按需加载 antd 的脚本和样式的
```
如果使用antd出现以下报错的话,修改antd版本为3.26.14
TypeError: antd_es_form__WEBPACK_IMPORTED_MODULE_7__.default.create(...) is not a function
```
```
$ npm install antd babel-plugin-import --save
```
编辑 .webpackrc,使 babel-plugin-import 插件生效。
```
{
"extraBabelPlugins": [
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
]
}
```
### 3.定义路由
我们要写个应用来先显示产品列表。首先第一步是创建路由,路由可以想象成是组成应用的不同页面
**3.1 创建路由页面**
在routes下面创建了两个文件夹分别为 indexPage 和 indexRouter 两个文件夹,内容类似下面代码
```
import React, { Component } from 'react'
export default class IndexRouter extends Component {
render() {
return (
indexRouter
)
}
}
```
在router.js里面分别把routes里面的路由页面引入进来,并且定义路由路径
```
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/indexPage/IndexPage';
import IndexRouter from './routes/indexRouter/IndexRouter';
function RouterConfig({ history }) {
return (
);
}
export default RouterConfig;
```
**3.2 切换 history 为 browserHistory**
先安装 history 依赖
```
$ npm install --save history
```
然后修改入口文件(index.js)
```
import { createBrowserHistory as createHistory} from 'history'
const app = dva({
history: createHistory(),
});
```
### 4.使用组件 Components
在components文件夹中书写子组件
----------子组件---------------
```
import React, { Component } from 'react'
export default class Example extends Component {
render() {
return (
商品详情:{this.props.title}
//this.porps.title接受父组件传过来的title
)
}
}
```
### 4.定义Model(类似react中的redux)
**4.1 新建 model models/products.js :**
```
export default {
namespace: 'product', //namespace - - 命名空间
state: { //state - - 数据状态
productList:[
{
name:'豆子'
},
{
name:'玉米'
}
]
},
reducers: { //reducer - - 修改状态
updateList(state,action) {
const productList = state.productList.concat(action.payload)
return { ...state, productList };
},
},
};
```
**4.2 connect连接model**
```
// 3. Model
app.model(require('./models/example').default);
app.model(require('./models/product').default);
```
**4.3 在父组件中**
```
import React from 'react'
import {connect} from 'dva' //使用connect方法将model和组建绑定
import Example from '../../components/Example' //引入子组件
class indexRouter extends React.Component {
render() {
const { productList ,dispatch } = this.props; //dispatch是model中的方法
return (
)
}
}
const mapStateToProps = (state) => {
return {
productList:state.product
}
}
export default connect(mapStateToProps)(indexRouter)
```
**4.4 在子组件中**
```
import React, { Component } from 'react'
export default class Example extends Component {
clickProductList = (e) => { //点击的时候往state中的product数组添加一条数据
const currentProduct = {
name:'玉米1'
}
this.props.dispatch({ //调用dispatch方法,type是namespace下的updateList方法,需要传一个参数写在payload中
type:'product/updateList',
payload:currentProduct
})
}
render() {
const {productList } = this.props.productList
return (
{
productList.map((item,index)=>{
return ( - {item.name}
)
})
}
)
}
}
```
### 5.路由跳转
```
第一种方式
import {Link} from 'dva/router'
去首页
第二种方式
如果是在子组件内点击跳转,因为子组件没有被路由包裹,所有没有history参数。
在父组件页面把this.props.history传入子组件, history={this.props.history}
子组件在需要跳转的按钮添加点击事件 onClick={this.clickProductList}
通过this.props.history.push进行跳转
clickPushIndex = (e) =>{
this.props.history.push('/')
}
第三种方式
如果在子组件里面,不通过父组件传递来过this.props.history的话
引入导出 withRouter
import {withRouter} from 'dva/router'
export default withRouter(Example)
第四种方式
import { routerRedux } from 'dva/router';
子组件在需要跳转的按钮添加点击事件 onClick={this.clickProducReduxtList}
clickProducReduxtList= (e) =>{
this.props.dispatch(routerRedux.push('/'))
}
```
### 6.Model异步操作
**6.1 在models下面的product.js中**
异步操作的方法可以看不懂可以查看dva官网知识地图中的Generators方法介绍,* 可以查看es6 的方法介绍
```
// 异步操作
effects:{
*updateListAsync({payload},{call,put}){
yield put({
type:'updateList',
payload //传的参数
})
}
}
```
**6.2 给点击时间增加异步操作**
```
clickProductListAsync = (e) =>{
const currentProduct = {
name:'玉米2'
}
this.props.dispatch({
type:'product/updateListAsync',
payload:currentProduct
})
}
```
### 7.Mock数据处理
**7.1 在util把request进行封装一下**
```
import fetch from 'dva/fetch';
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;
}
const parseQuery = (obj) => {
let str = ''
for (let key in obj) {
const value = typeof obj[key] !== 'string' ? JSON.stringify(obj[key]) : obj[key]
str += '&' + key + '=' + value
}
return str.substr(1)
}
/**
* 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"
*/
const request = (url, method = 'get', data) => {
const options = {
method: method, // HTTP请求方法,默认为GET
headers: { // HTTP的请求头,默认为{}
'Content-Type': 'application/json'
},
credentials: 'include' // 是否携带cookie,默认为omit,不携带; same-origi,同源携带; include,同源跨域都携带
}
if (method === 'get') {
url += '?' + parseQuery(data)
} else {
options.body = JSON.stringify(data)
}
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => ({ data }))
.catch(err => ({ err }));
}
export default {
get (url, data) {
return request(url, 'get', data)
},
post (url, data) {
return request(url, 'post', data)
}
}
```
**7.2 在.roadhogrc.mock.js把mock文件夹下的所有js全部引入**
```
// 可以把mock里面所有的js都导出
import fs from 'fs'
import path from 'path'
const mock = {}
fs.readdirSync(path.join(__dirname + '/mock')).forEach(function(file){
if(file.match(/\.js$/)){
Object.assign(mock,require('./mock/' +file))
}
});
export default mock
```
**7.3 实例操作**
在mock文件夹下面创建一个product.js文件
```
module.exports = {
"get /api/product" : {"name":"高粱"}
}
```
在services文件夹下的example.js下把product方法导出
```
import httpApi from '../utils/request';
export function getProduct(){
return httpApi.post("/api/product")
}
```
在组件中使用
```
import * as api from '../services/example'
api.getProduct()
.then(res=>{
console.log(res)
})
```
### 7.网络请求
**7.1 在models下面的product.js中api引入**
```
import * as api from '../services/example'
// 网络请求方法
*updateListHttp({payload},{call,put}){
const result = yield call(api.getProduct,payload)
const data = result.data;
if(data){
yield put({
type:'updateList',
payload:data //传的参数
})
}
}
```
**7.2 页面对方法进行调用**
```
clickProductListHttp = (e) =>{
console.log('http')
this.props.dispatch({
type:'product/updateListHttp',
payload:{
id:1001 //传的参数
}
})
}
```
**7.3 在services文件夹下的example.js中需要传一个data**
```
import httpApi from '../utils/request';
export function getProduct(data){
return httpApi.get("/api/product",data)
}
```
**7.3 在mock文件夹下面的 product.js获取传入的参数**
```
module.exports = {
"get /api/product" :(req,res)=>{
const params = req.query
res.send({"name":"高粱"})
}
}
```
### 8.遍历Model
在model文件夹下面创建一个index.js
```
const context = require.context('./',false,/\.js$/)
export default context
.keys()
.filter(item => item != './index.js')
.map(key => context(key))
```
把主文件index.js下面的 3. Model文件可以替换一下
```
require('./models').default.forEach(key => app.model(key.default))
```
### 结尾
### 推荐大家可以查看redux-action