Vue 中使用mockjs模拟后端数据

关于mockjs,官网描述的是

1.前后端分离

2.不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。

3.数据类型丰富

4.通过随机数据,模拟各种场景。

等等优点。

总结起来就是在后端接口没有开发完成之前,前端可以用已有的接口文档,在真实的请求上拦截ajax,并根据mockjs的mock数据的规则,模拟真实接口返回的数据,并将随机的模拟数据返回参与相应的数据交互处理,这样真正实现了前后台的分离开发。

与以往的自己模拟的假数据不同,mockjs可以带给我们的是:在后台接口未开发完成之前模拟数据,并返回,完成前台的交互;在后台数据完成之后,你所做的只是去掉mockjs:停止拦截真实的ajax,仅此而已。
接下来就一步一步实现这个过程:

1.引入js依赖

npm install mockjs --save-dev

2.建一个mock文件夹来统一管理我们的mock数据

3.在mockjs下面新建几个模拟数据文件

import Mock from 'mockjs'
 
const data = Mock.mock({  // 模拟数据生成,遵循Mock语法规范
  'items|30': [{
    id: '@id',
    title: '@sentence(10, 20)',
    'status|1': ['published', 'draft', 'deleted'],
    author: 'name',
    display_time: '@datetime',
    pageviews: '@integer(300, 5000)'
  }]
})
 
export default [  
  // 路由拦截,返回指定格式数据
  // 以下格式为兼容after中间件拦截、返回要求
  {
    url: '/table/list',
    type: 'get',
    response: config => {
      const items = data.items
      return {
        code: 20000,
        data: {
          total: items.length,
          items: items
        }
      }
    }
  }
  ... // 更多
]

 

4.在mock文件夹下建一个index.js

5.在mock/index.js中写关键代码,拦截到我们前端发出的请求

const Mock = require("mockjs");
const { param2Obj } = require("./utils"); //解析地址栏参数的函数
// 导入模拟数据
const creditEvaluateStatistics = require("./credit-evaluate-statistics");

const mocks = [...creditEvaluateStatistics];

// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
function mockXHR() {
  // mock patch
  // https://github.com/nuysoft/Mock/issues/300
  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send;
  Mock.XHR.prototype.send = function () {
    if (this.custom.xhr) {
      this.custom.xhr.withCredentials = this.withCredentials || false;

      if (this.responseType) {
        this.custom.xhr.responseType = this.responseType;
      }
    }
    this.proxy_send(...arguments);
  };

  function XHR2ExpressReqWrap(respond) {
    console.log("respond:", respond);
    return function (options) {
      console.log("options:", options);
      let result = null;
      if (respond instanceof Function) {
        const { body, type, url } = options;
        // https://expressjs.com/en/4x/api.html#req
        result = respond({
          method: type,
          body: JSON.parse(body),
          query: param2Obj(url),
        });
      } else {
        result = respond;
      }
      return Mock.mock(result);
    };
  }
// 批量注册路由事件
  for (const i of mocks) {
    console.log(i);
    Mock.mock(
      new RegExp(i.url),
      i.type || "get",
      XHR2ExpressReqWrap(i.response)
    );
  }
}

module.exports = {
  mocks,
  mockXHR,
};

 

utile.js文件
/**
 * @param {string} url
 * @returns {Object}
 */
function param2Obj(url) {
  const search = decodeURIComponent(url.split("?")[1]).replace(/\+/g, " ");
  if (!search) {
    return {};
  }
  const obj = {};
  const searchArr = search.split("&");
  searchArr.forEach((v) => {
    const index = v.indexOf("=");
    if (index !== -1) {
      const name = v.substring(0, index);
      const val = v.substring(index + 1, v.length);
      obj[name] = val;
    }
  });
  return obj;
}

/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
function deepClone(source) {
  if (!source && typeof source !== "object") {
    throw new Error("error arguments", "deepClone");
  }
  const targetObj = source.constructor === Array ? [] : {};
  Object.keys(source).forEach((keys) => {
    if (source[keys] && typeof source[keys] === "object") {
      targetObj[keys] = deepClone(source[keys]);
    } else {
      targetObj[keys] = source[keys];
    }
  });
  return targetObj;
}

module.exports = {
  param2Obj,
  deepClone,
};
6.main.js文件中引入
// main.js 开启mock 服务
import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'development') {
  mockXHR()

7.封装请求方法

import axios from "axios";
import { Loading, Message } from "element-ui";
import store from "@/store";
// import { getToken } from "@/utils/auth";

// create an axios instance
const service = axios.create({
  // baseURL: "", 
  timeout: 10000, // request timeout
  // headers: {
  //   "Content-Type": "multipart/form-data",
  // },
});

let apiCallNo = 0;
let loadingInstance;

// request interceptor
// TODO 待优化
service.interceptors.request.use(
  (config) => {
    if (config.data) {
      const { hideLoading, ...rest } = config.data;
      if (!hideLoading) {
        apiCallNo += 1;
        if (apiCallNo === 1) {
          loadingInstance = Loading.service();
        }
      }
      if (Object.keys(rest).length !== 0) {
        config.data = rest;
      } else if (typeof hideLoading === "boolean") {
        config.data = null;
      }
    } else {
      apiCallNo += 1;
      if (apiCallNo === 1) {
        loadingInstance = Loading.service();
      }
    }

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      // config.headers["X-Token"] = getToken();
    }
    return config;
  },
  (error) => {
    // do something with request error
    return Promise.reject(error);
  }
);

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  (response) => {
    apiCallNo -= 1;
    if (apiCallNo === 0) {
      loadingInstance.close();
    }
    const res = response.data;

    // 导出二进制流数据
    if (res.type) {
      return res;
    }
    // 普通请求
    if (res.status !== 200) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000,
      });
      return Promise.reject(new Error(res.message || "Error"));
      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
      //   // to re-login
      //   MessageBox.confirm(
      //     "You have been logged out, you can cancel to stay on this page, or log in again",
      //     "Confirm logout",
      //     {
      //       confirmButtonText: "Re-Login",
      //       cancelButtonText: "Cancel",
      //       type: "warning",
      //     }
      //   ).then(() => {
      //     store.dispatch("user/resetToken").then(() => {
      //       location.reload();
      //     });
      //   });
      // }
    } else {
      return res.data;
    }
  },
  (error) => {
    console.log(error.response);
    apiCallNo -= 1;
    if (apiCallNo === 0) {
      loadingInstance.close();
    }
    Message({
      message: error.response?.data.message ?? "网络异常,请重试", // TODO 是否要改成统一的提示?
      type: "error",
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

export default service;

8.新建一个文件专门封装api

import request from "@/utils/request"; // 引入request方法
// 使用mock模拟后端数据
export function fetchResultTrend(params) {
  return request({
    url: "/mock/credit-evaluate-statistics/result/trend",
    params,
  });
}

9.在vue文件中调用接口

async closerRateChange() {
      const res = await fetchResultTrend();
      console.log(res)
}

经过以上步骤我们就顺利实现了mockjs模拟数据的过程。
mockjs官网地址:
http://mockjs.com/examples.html
https://github.com/nuysoft/Mock/wiki/Getting-Started

posted @ 2020-09-05 14:15  leahtao  阅读(1516)  评论(0编辑  收藏  举报