用 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
1、在 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;
2、简单封装 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.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" */ 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 })) }
1、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), }) }
1、在 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 }, }
/* 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(); }, }, };
1、例如在 src/pages/myPage/index.js 页面发送请求,并在页面中显示请求到的数据
2、如果在第4步中,选择将接口返回的数据存在 model 中,此页面就可以直接从 model 中获取数据,不需要存到 state 中
import React, { Component } from "react"; import { Button } from ‘antd‘; import { connect } from ‘dva‘; 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 中的数据。 }; export default connect(mapStateToProps)(secondPage)
结尾:对于一个没人教的菜鸟来说,摸索出这一套,真的相当费劲,断断续续的隔了好几周,终于完成了。
此文章复制可用,如果对你也有用,请一定要让我知道,万分感谢。
原文:https://www.cnblogs.com/MrZhujl/p/13497272.html