前言
业务需求: 在前后端分离的开发模式中,需要前后端同时进入开发,但是在后端接口完成前,暂时没有可用的接口给前端使用的,如果先写静态页面后期再改的话,就会有重复工作。所以我们需要一种简单快速模拟数据的方式来协助开发。
实现思路: 接口文档的api及数据格式确认好之后,在前端采用Mock服务来模拟请求后台接口,按照接口文档的约定写入api、入参及出参等假数据,来模拟整个前后端联调的过程。
一、Mock简介
Mock.js官网提供的Mock示例及Wiki文档,对Mock的使用描述的非常详细,并且可以在控制台快速试验这些方法。
二、Vue 项目中使用 mock.js 拦截数据
注:使用vue init project-name 创建的Vue项目
1、安装相关依赖
npm install axios –save
npm install mockjs --save-dev
2、模拟Mock数据
- 通过配置devServer.before选项,设置url访问路径及response响应数据,进行mock数据。
- 使用Mock.mock(),根据数据模板生成模拟数据。
2.1 Mock数据模拟方式一:devServer.before
-
在src文件夹下新建mock文件夹及index.js,在
index.js
中统一处理所有的mock数据
-
在index.js文件中引入mock、声明随机数据、暴露拦截方法
const Mock = require('mockjs') // 引入mockjs
module.exports = function (app) { // 暴露一个函数,用于拦截请求时触发
//监听http请求: app是一个请求实例,.get方法第一个参数传需要拦截的url,第二个参数传一个函数,该函数有两个参数(也可用app.post)
app.get('/user/userInfo', (rep, res) => {
// 设置要返回的数据(用mock随机生成的数据)
let json = {
id: '@id()', // 得到随机的id
username: '@cname()', // 随机生成中文名字
email: '@email()', // email
ip: '@ip()', // ip地址
description: '@paragraph()', // 描述
date: '@date()' // 随机生成日期
}
// 通过res.json将上面声明的随机数据转为json并作为请求的返回值返回
res.json(Mock.mock(json))
})
}
- 配置config进行拦截
在config.js
(build->webpack.dev.conf.js或vue.config.js)中配置拦截,使用devServe.before钩子函数,用来监听来自web的http请求(在所有请求前先执行该函数)。引入刚刚编辑好的mock->index.js,该js文件暴露的函数将在这里调用。
(我这里是在build->webpack.dev.conf.js中配置的)
module.exports = {
devServer: {
before: require('./mock/index.js') // 引入 mock/index.js
}
}
2.2 Mock数据模拟方法二:Mock.js
- 在src文件夹下新建mock文件夹及index.js,在
index.js
中统一处理所有的mock数据 - 在index.js文件中引入mock、声明随机数据、暴露拦截方法
import Mock from 'mockjs'
//表示需要拦截的 URL,可以是 URL 字符串或 URL 正则。
//表示需要拦截的 Ajax 请求类型。例如 GET、POST、PUT、DELETE 等。
//表示数据模板,可以是对象或字符串。例如 { 'data|1-10':[{}] }、'@EMAIL'。
Mock.mock('/user/userInfo', 'get', {
id: '@id()', // 得到随机的id
username: '@cname()', // 随机生成中文名字
email: '@email()', // email
ip: '@ip()', // ip地址
description: '@paragraph()', // 描述
date: '@date()' // 随机生成日期
})
- 在main.js中引入
require("./mock/index.js")
3、在vue使用
发送http请求的URL与mock中定义的URL需一致
import axios from "axios";
export default {
mounted() {
axios.get('/user/userInfo').then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
}
}
4、效果
PS:如果发送http请求的URL与mock中定义的URL不匹配或不存在的话,就会报404错误!
请确认URL没问题。
三、Mock使用优化
1、mock数据使用单独的js文件
1.1 场景描述
所有接口的mock数据都在index.js里处理,在接口太多的情况下查找及修改很不方便,因此将模拟的mock数据单独放入对应的js文件(可按接口或业务需求进行处理)。
1.2 解决方案
- 在mock文件夹下新建userInfo.js,在userInfo.js写入该接口模拟的mock数据。
/**
* 写法一
*/
// module.exports = {
// id: '@id()', // 得到随机的id
// username: '@cname()', // 随机生成中文名字
// email: '@email()', // email
// ip: '@ip()', // ip地址
// description: '@cparagraph()', // 描述
// date: '@date()' // 随机生成日期
// }
/**
* 写法二
*/
// const json = {
// id: '@id()', // 得到随机的id
// username: '@cname()', // 随机生成中文名字
// email: '@email()', // email
// ip: '@ip()', // ip地址
// description: '@cparagraph()', // 描述
// date: '@date()' // 随机生成日期
// }
// module.exports = json
/**
* 写法三
*/
const userInfo = () => {
const json = {
id: '@id()', // 得到随机的id
username: '@cname()', // 随机生成中文名字
email: '@email()', // email
ip: '@ip()', // ip地址
description: '@cparagraph()', // 描述
date: '@date()' // 随机生成日期
}
return { data: { result: json, code: 200, message: 'success' } }
}
module.exports = {
getUserInfo: userInfo
}
- 修改mock->index.js文件,使用
require('./userInfo.js')
来获取模拟的json数据
const Mock = require('mockjs') // 引入mockjs
module.exports = function (app) { // 暴露一个函数,用于拦截请求时触发
//监听http请求: app是一个请求实例,.get方法第一个参数传需要拦截的url,第二个参数传一个函数,该函数有两个参数(也可用app.post)
app.get('/user/userInfo', (rep, res) => {
// 设置要返回的数据(用mock随机生成的数据)
// let json = {
// id: '@id()', // 得到随机的id
// username: '@cname()', // 随机生成中文名字
// email: '@email()', // email
// ip: '@ip()', // ip地址
// description: '@cparagraph()', // 描述
// date: '@date()' // 随机生成日期
// }
// const json = require('./userInfo.js') // userInfo.js直接返回json数据(对应写法一、二)
const json = require('./userInfo.js').getUserInfo() // userInfo.js返回函数(对应写法三)
// 通过res.json将上面声明的随机数据转为json并作为请求的返回值返回
res.json(Mock.mock(json))
})
}
2、mock获取get、post请求参数
2.1 场景描述
在前后端分离的开发模式下,想要尽可能的模拟真实情况,如不同的请求方式get/post,接口超时响应等。
2.2 解决方案
2.2.1 设置延时请求到数据
1.不设置延时很有可能遇到坑【敲黑板】,因为真实的请求是需要时间的,mock不设置延时是拿到数据马上返回。
2.要模拟Loading加载效果时。
//设置延迟响应,模拟向后端请求数据
Mock.setup({
timeout: 800
})
2.2.2 get请求参数
Mock.mock("/api/order/index", "get", (options) =>{
let result = {} // 数据或对象
return { data: { result: result, code: 200, message: 'success' } }
});
注意:get 请求如果带参数的话,是在URL后面加上参数,因此和设置的URL没有匹配上,会导致URL没找到请求失败。
举个例子:使用 Mock.mock(“/user/getUserInfo”, “get”, mockData) 时,它只会拦截URL等于/user/getUserInfo的请求,而对于带参数的请求,如/user/getUserInfo?id=123就拦截不到。
建议将URL改写成正则表达式的URL
Mock.mock(RegExp(url + ".*"), "get", mockData);
Mock.mock(RegExp("/api/order/index"+ ".*");, "get", (options) =>{
let result = {} // 数据或对象
return { data: { result: result, code: 200, message: 'success' } }
});
2.2.3 post请求参数
post请求可以是 URL 字符串或 URL 正则。
Mock.mock("/api/order/index", "post", (options) =>{
let result = {} // 数据或对象
return { data: { result: result, code: 200, message: 'success' } }
});
3、mock返回列表及分页数据
3.1 场景描述
在mock文件夹下新建order.js,以订单列表为例,我们模拟了订单列表数据,但列表数据还需分页才能满足业务需求。
3.2 解决方案
在现有的列表数据基础上进行改造,增加分页功能。
- 未分页的orderLIst
const orderList = () => {
const dataList = []
for (let i = 0; i < 100; i++) {
const newObject = {
orderId: '@id()', // 得到随机的id
orderType: '@cword("全次极加延", 1)', // 得到随机的文本
createTime: '@datetime()' // 随机生成日期
}
dataList.push(newObject)
}
return { data: { result: dataList, code: 200, message: 'success' }}
}
module.exports = {
getOrderList: orderList
}
- 分页的orderLIst
const orderList = (params) => {
const dataList = []
for (let i = 0; i < 100; i++) {
const newObject = {
orderId: '@id()', // 得到随机的id
orderType: '@cword("全次极加延", 1)', // 得到随机的文本
createTime: '@datetime()' // 随机生成日期
}
dataList.push(newObject)
}
const { page, pageSize } = params
const total = dataList.length
const len = total / pageSize
const totalPage = Number.isInteger(len) ? len + 1 : len
const list = dataList.slice((page - 1) * pageSize, page * pageSize)
return {
data: {
result: {
page,
pageSize,
total,
totalPage,
list
}, code: 200, message: 'success'
}
}
}
module.exports = {
getOrderList: orderList
}
4、控制mock在开发环境使用,在生产环境禁用
4.1 场景描述
mock拦截所有的axios请求,根据请求做出相应的响应。在前后端分离开发时我们使用mock获得相应的数据,但当和后端联调时不禁用mock的话,就无法获得后端接口数据,因此我们来配置一个mock模拟数据的开关。
4.2 解决方案
- 在vue中通过设置main.js中的Vue.config.productionTip来决定模式。默认为false是生产环境。
- 我们在config/dev.env.js和config/prod.env.js中设置变量。
// dev.env.js下的配置。
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
MOCK: true //开发环境使用mock
})
// prod.env.js下的配置
module.exports = {
NODE_ENV: '"production"',
MOCK: false // 生产环境禁用mock
}
- 在main.js中设置
process.env.MOCK && require("./mock/index.js")
。
process.env.MOCK这句就是判断刚才设置的值,如果是true,才会执行语句引入mock,如果是false,则后面的语句不执行,即不引入mock。
总结
Mock原理:前端发送请求后,在devServe.before中运行mock/index.js暴露的函数,捕捉到url一致的请求,直接返回mock设定的数据,有返回值就不会继续真实的请求,拦截成功之后,请求没有往后台去,而是在前端完成了虚拟请求。