mock.js的用处是增加单元测试的真实性,通过随机数据,模拟各种场景。不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。
最关键的一点是不需要等待后端接口,可以自己模拟响应的数据来开发前端
这无疑会给前端开发带来方便
mock.js的安装
运行
npm install mockjs
在项目根目录新建mock文件夹,与package.json同级
在mock文件夹下新建index.js
const Mock = require(‘mockjs‘)
const { param2Obj } = require(‘./utils‘)
const test = require(‘./test‘)
const mocks = [
...test,
]
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) {
Mock.mock(new RegExp(i.url), i.type || ‘get‘, XHR2ExpressReqWrap(i.response))
}
}
module.exports = {
mocks,
mockXHR
}
为了不让mock.js每次修改的时候都需要重新打包,我们可以写一个mock-server热修改功能
const chokidar = require(‘chokidar‘)
const bodyParser = require(‘body-parser‘)
const chalk = require(‘chalk‘)
const path = require(‘path‘)
const Mock = require(‘mockjs‘)
const mockDir = path.join(process.cwd(), ‘mock‘)
function registerRoutes(app) {
let mockLastIndex
const { mocks } = require(‘./index.js‘)
const mocksForServer = mocks.map(route => {
return responseFake(route.url, route.type, route.response)
})
for (const mock of mocksForServer) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocksForServer).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
}
})
}
// for mock server
const responseFake = (url, type, respond) => {
console.log(new RegExp(`/dev-api/${url}`));
return {
url:new RegExp(`/dev-api/${url}`),
type: type || ‘get‘,
response(req, res) {
console.log(‘request invoke:‘ + req.path)
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
}
}
}
module.exports = app => {
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
}).on(‘all‘, (event, path) => {
if (event === ‘change‘ || event === ‘add‘) {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
}
}
})
}
定义工具类util.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
}
module.exports = {
param2Obj
}
mock.js的使用
我们首先在mock文件夹下编写test.js,用于测试接口
const testApi1 = {
"userList|10":[
{
"name":"@cname",
"age|18-28":25,
"sex|1":["男","女"],
}],
}
module.exports = [
{
url: ‘data/testApi1‘,
type: ‘get‘,
response: config => {
// const { username } = config.body
// const token = tokens[username]
return {
code: 200,
data: testApi1
}
}
},
]
这里我们拦截data/testApi1的get请求,并返回数据
数据的格式是随机生成的10个对象,包含人名、年龄、性别等。
找到request.js,将axios的基本路径保持和mock-server的监听基本路径一致
import axios from ‘axios‘
export function request(config) {
return new Promise(((resolve, reject) => {
const instance = axios.create({
baseURL: "/dev-api/", //基本路径
timeout: 1000,
});
instance(config).then(res => {
resolve(res);
}).catch(err => {
reject(err);
})
}))
}
之后修改vue.config.js文件
module.exports = {
// 输出目录
assetsDir: ‘static‘,
devServer: {
port: 8080,
open: true,
overlay: {
warnings: false,
errors: true
},
before: require(‘./mock/mock-server.js‘)
},
};
需要添加devServer:{before: }选项,让mock.js起到拦截作用
最后我们编写一个请求,模拟向后端请求数据
getData() {
request({
url: ‘data/testApi1‘,
method: ‘get‘,
}).then(res => {
let {code, data} = res.data;
console.log(data);
this.testData = data.userList;
}).catch(err => {
console.log("fail")
});
}