关于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