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 请求简单的封装

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 }))
}

二、写 api

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),
    })
}

三、mock 相关配置

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 },
}

四、编写 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发送请求

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