话不多说,把手拿来。
珍爱生命,远离后台。
在前后端分离模式项目开发过程中,前端的界面展示、交互逻辑往往需要后台接口数据支撑,然而万恶的后台总跟不上有我们美丽的前端小姐姐进度。
于是我们不由得仰天长叹,难道就没有即无需依赖后台又能完美展示界面数据,即能保证前端交互的完整性又不太费事儿的方法吗?
答案当然是有的~~~~ 下面就请我们的主角登场
Mock官网首页是这么定义的:生成随机数据,拦截 Ajax 请求。
传送门 & 示例
前后端分离其实从增加了开发成本,只是在它带来的优势上可以让我们忍受这种成本,但攻克这方面成本的目标却从未停止
做完本阶段的开发详设之后,前端就可以开时本阶段的开发了,由于基础设定已经在详设阶段完成统一,前后端在开发过程中应该是可以完全独立的。但实际开发中前端往往留着某些接口回执逻辑等着与后台对接后填充,无法完全完成前端自身的界面展示及交互逻辑。而使用mockjs模拟接口及数据前端可以在最大程度上彻底分离开发时与后台的耦合成本,在开发时就基本完成全部前端逻辑编写
前后端完成独立开发后,就进入了前后台对接阶段,很多项目组就在此阶段会花费大量的调试时间及成本。然而实际上,如果前端做完了数据及交互的逻辑,后台完成了所有接口自测,此阶段应该是非常迅速的
很多前端开源项目都是无后台,模拟数据演示的。例如vue-element-admin,d2-admin,wl-admin.
npm install mockjs
import Mock from 'mockjs'
随机生成一个20-40条的数组数据试试
const projectList = Mock.mock({
"list|20": [{
'name': '@cname', // 中文名
'account': `@word`, // 英文单词
'phone': /1[3-9][0-9]{9}/, // 正则模式
'deptName': Mock.mock('@cword(2,4)'), // 随机2-4字中文单词
'id': '@guid', // guid
}]
})
有多种模式可选,具体见官方示例。
有了数据之后,我们怎么把数据模拟通过接口返回呢
export default [
{
url: '/Api/Project/GetList',
type: 'post',
response: (res) => {
let _fileter_list = [];
if(res.body.key){
let _fileter_list = projectList.fileter(i => i.name == res.body.key)
}
// 没错,你应该已经猜到了,res.body就是我们传入到接口的数据,我们可以在这里做些逻辑操作
// res包含完整的请求头信息
return {
code: 200,
message: "操作成功",
data: _fileter_list
}
// 使用return返回前端需要的数据
}
}
... // 多个接口
]
在mock/文件夹下再建一个index.js写我们mock的监听逻辑
import Mock from 'mockjs' // 导入mockjs
import demoApi from './demo' // 导入我们模拟数据的js文件
const mocks = [
{
intercept: true, // 你可能需要一个开关,来使模拟请求与真实请求并存
fetchs: demoApi
},
// 抄来一个解析地址栏参数解析函数
export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
// 关键!抄来一个前端模式构建函数(或者你也可以建一个mock server)
export function mockXHR() {
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) {
return function(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) {
if(i.intercept){
for(const fetch of i.fetchs){
Mock.mock(new RegExp(fetch.url), fetch.type || 'get', XHR2ExpressReqWrap(fetch.response))
}
}
}
}
经过上面一通代码之后,我们已经初步完成了前端模拟数据的技术条件。 import { mockXHR } from '../mock'
if(process.env.NODE_ENV == 'development'){
mockXHR();
}
axios.post('/Api/Project/GetList').then(res => {
console.log(res)
})
但是,项目中我们并不建议你直接就这么做!src下建一个api文件夹
然后再在src/api/下建一个_request.js
import Axios from "axios";
// 定义axios配置
const http = Axios.create({
baseURL: '', // api的base_url
withCredentials: true, // 开启跨域身份凭证
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8"
},
timeout: 5000 // request timeout
});
// 设置全局的请求次数,请求的间隙,用于自动再次请求
http.defaults.retry = 2;
http.defaults.retryDelay = 1000;
// 请求拦截器
http.interceptors.request.use(
function (config) {
return config;
},
function (error) {
return Promise.reject(error);
}
);
// 响应拦截器
http.interceptors.response.use(
function (res) {
return res;
},
function (err) {
let config = err.config;
let errres = err.response;
let err_type = errres ? errres.status : 0;
// 收集错误信息
switch (err_type) {
case 400:
err.message = "请求无效";
break;
case 401:
err.message = "由于长时间未操作,登录已超时,请重新登录";
break;
case 403:
err.message = "拒绝访问";
break;
case 404:
err.message = `请求地址出错: ${errres.config.url}`;
break;
case 405:
err.message = `未授权`;
break;
case 408:
err.message = "请求超时";
break;
case 500:
err.message = "服务器内部错误";
break;
case 501:
err.message = "服务未实现";
break;
case 502:
err.message = "网关错误";
break;
case 503:
err.message = "服务不可用";
break;
case 504:
err.message = "网关超时";
break;
case 505:
err.message = "HTTP版本不受支持";
break;
default:
err.message = "网络波动,请重试";
}
// If config does not exist or the retry option is not set, reject
if (!config || !config.retry) return Promise.reject(err);
// Set the variable for keeping track of the retry count
config.__retryCount = config.__retryCount || 0;
// Check if we've maxed out the total number of retries
if (config.__retryCount >= config.retry) {
// Reject with the error
return Promise.reject(err);
}
// Increase the retry count
config.__retryCount += 1;
// Create new promise to handle exponential backoff
let backoff = new Promise(function (resolve) {
setTimeout(function () {
resolve();
}, config.retryDelay || 1);
});
// Return the promise in which recalls axios to retry the request
return backoff.then(function () {
return http(config);
});
}
);
export default http;
我们在api/文件夹下再建一个project.js用来统一管理项目模块的所有接口
import request from "../_request";
// 1获取部门列表接口
function getProjectListApi(data) {
return request({
url: "/Api/Project/GetList",
method: 'post',
data
});
}
// 2添加项目接口
function addProjectApi(data) {
return request({
url: "/Api/Project/Add",
method: 'post',
data
});
}
// 3删除项目接口
function delProjectApi(data) {
return request({
url: "/Api/Project/Add",
method: 'post',
data
});
}
export {
getProjectListApi, // 1获取部门列表接口
addProjectApi, // 2添加项目接口
delProjectApi, // 3删除项目接口
}
```
import { getProjectListApi } from "@/api/project.js"; // 导入用户列表接口
export default {
data(){
return {
projectList: []
}
},
created(){
this.getProjectList()
},
methods: {
getProjectList(){
getProjectListApi().then(res => {
console.log(res)
})
}
}
}
```
不出意外的话,经过上面的一系列操作,我们的数据已经出来啦,mockjs如何在项目中的应用也已经入门完毕,如果你还想被我摸手,就来我的Github交个朋友吧~
Github & segmentfault & 掘金 & csdn & 语雀