umi 如何使用 Mock 模拟数据
用 umi 搭建的 react 项目中,看 package.json 文件,我们可以看到:
"dependencies": {
"dva": "^2.6.0-beta.6",
"antd": "^3.19.5",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
其实 umi 中就使用了 dva 。
先看一下目录结构,我会标注出需要用到的文件:
├── dist/ // 默认的 build 输出目录
├── mock/ // mock 文件所在目录,基于 express
├── config/
├── config.js // umi 配置,同 .umirc.js,二选一
└── src/ // 源码目录,可选
├── layouts/index.js // 全局布局
├── models // 全局的数据仓库 类似于 redux
├── pages // 页面目录,里面的文件即路由
├── myPage // 我创建的第一个文件夹
├── index.js // 入口文件
├── index.less
└── model.js // 页面级别的数据仓库,相当于 页面级别的 redux
├── .umi // dev 临时目录,需添加到 .gitignore
├── .umi-production // build 临时目录,会自动删除
├── document.ejs // HTML 模板
├── 404.js // 404 页面
├── page1.js // 页面 1,任意命名,导出 react 组件
├── page1.test.js // 用例文件,umi test 会匹配所有 .test.js 和 .e2e.js 结尾的文件
└── page2.js // 页面 2,任意命名
├── services
└── api.js // *放接口
├── utils
├── config.js // *配置路径
└── request.js // *封装 fetch 方法
├── global.css // 约定的全局样式文件,自动引入,也可以用 global.less
├── global.js // 可以在这里加入 polyfill
├── app.js // 运行时配置文件
├── .umirc.js // umi 配置,同 config/config.js,二选一
├── .env // 环境变量
├── .gitignore // 避免将不必要的代码提交到 git 仓库中
└── package.json
如果你做的是一个新项目,请不要跳过第一步。
一、fetch 请求简单的封装
- 在 utils 文件夹下创建 config.js 文件,utils/config.js ,对fetch请求路径进行配置
//config.js文件
const config = {
apiUrl: process.env.NODE_ENV === 'development' ? ' http://127.0.0.1:7001' : 'https://www.baidu.com',
apiPrefix: ' http://127.0.0.1:7001',
proxy: true //是否开启mock代理
};
export default config;
- 简单封装 fetch 请求,utils文件夹下的 request.js 文件
//request.js文件
// import fetch from 'dva/fetch';
import config from './config';
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 assyParams = (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.substring(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"
*/
export default function request(obj) {
let url = '';
let options = {
method: obj.method,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
credentials: 'include' //是否携带cookie,默认为omit不携带; same-origi同源携带; include同源跨域都携带
};
if (obj.method === 'GET' || obj.method === 'get') {
url = (config.proxy ? obj.url : config.apiUrl + obj.url) + '?' + assyParams(obj.data);
}
if (obj.method === 'POST' || obj.method === 'post') {
url = config.proxy ? obj.url : config.apiUrl + obj.url;
options.body = JSON.stringify(obj.data);
}
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => ({ data }))
.catch(err => ({ err }))
}
二、写 api
- service 文件下创建 api.js
import request from '../utils/request';
export function getSomeData(params) {
return request({
method: "GET",
url: `/appservice/common/v1/getSomeData`,
data: JSON.stringify(params),
})
}
三、mock 相关配置
- 在 mock 文件夹下新建 someData.js 文件 (文件名看工作需要修改)
const responseData = {
status: 'ok',
code: 200,
data: "这是数据"
}
export default {
// 支持值为 Object 和 Array
'GET /appservice/common/v1/getSomeData': responseData,
// GET POST 可省略 比如:
'/api/users/1': { id: 1 },
}
四、编写 src/pages/myPage/model.js
/*
export default {
namespace: '', // 表示在全局 state 上的 key
state: {}, // 状态数据
reducers: {}, // 管理同步方法,必须是纯函数
effects: {}, // 管理异步操作,采用了 generator 的相关概念
subscriptions: {}, // 订阅数据源
};
call: 执行异步函数
put: 发出一个 Action,类似于 dispatch
select: 返回 model 中的 state
*/
import {
getSomeData,
} from '../../services/api';
function initState() {
return {
modelNum: 0,
text: "没有返回"
};
}
export default {
namespace: 'myPage', // 表示在全局 state 上的 key
state: initState(), // 状态数据
effects: { // 管理异步操作,采用了 generator 的相关概念
*getSomeData({ payload }, { call, put, select }) {
const res = yield call(getSomeData, payload);
if (res.data.code === 200) { // 拿到数据,可以选择存到 model 中
yield put({
type: 'saveDefault',
payload: {
text: res.data.data
},
});
}
return res;
},
},
reducers: { // 管理同步方法,必须是纯函数
saveDefault(state, action) {
return {
...state,
...action.payload,
};
},
resetState() { // 重置 state
return initState();
},
},
};
五、调用api发送请求
- 例如在 src/pages/myPage/index.js 页面发送请求,并在页面中显示请求到的数据
- 如果在第4步中,选择将接口返回的数据存在 model 中,此页面就可以直接从 model 中获取数据,不需要存到 state 中
import React, { Component } from "react";
import { Button } from 'antd';
import { connect } from 'dva';
// import { connect } from 'react-redux';
class secondPage extends Component {
constructor(props) {
super(props);
this.state = {
data: null
}
}
getSomeData = async () => {
const { dispatch } = this.props;
await dispatch({
type: 'myPage/getSomeData'
}).then((res) => {
if (res && res.data && res.data.code === 200) {
this.setState({ // 这里选择将数据存在 state 中,也可以从 model 中获取
data: res.data.data
})
}
})
}
render() {
const { data } = this.state;
return (
<div>
<div>
<span>异步请求的返回:{data || "--"}</span>
<Button onClick={() => this.getSomeData()} >请求接口</Button>
</div>
</div>
)
}
}
const mapStateToProps = (model) => {
console.log(model) // 查看 props 中的数据,可以拿到存在 model 中的数据。
return model.text // return 出来的数据,会被添加到对应的 props 中
};
export default connect(mapStateToProps)(secondPage)
/*
拿到 dispatch 的几种方式
import { useDispatch } from 'react-redux';
import { useDispatch } from 'dva';
export default function (props) {
const dispatch = useDispatch();
return (
<div>
</div>
);
}
*/
结尾:对于一个没人教的菜鸟来说,摸索出这一套,真的相当费劲,断断续续的隔了好几周,终于完成了。
此文章复制可用,如果对你也有用,请一定要让我知道,万分感谢。