關於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