项目开发过程当中,一般先定义接口格式,先后端并行开发。若是前端先开发完成,由于接口还没有实现,只能在代码中写一些测试数据来测试页面效果。这致使了测试数据侵入业务代码,后期上线时还得删去。如今的中大型应用一般会使用 vuex
和 redux
等状态管理仓库,这种状况下测试数据写起来麻烦,由于可能须要修改部分逻辑来配合测试数据,后期去除时极易误删或遗漏改正测试逻辑。同时,这种测试方式,部分与请求相关的 bug 难以检测出 。这致使后期联调时前端压力重。html
node
命令行参数,动态地给 webpack
添加入口来实现是否打包 mock 工具及数据mockjs
是一个前端本地 mock 工具,其原理是对 XMLHttpRequest
对象进行改写,在请求发出前若是检测到请求的接口已注册 mock 规则,则返回设定好的测试数据,实际并无发出 AJAX
请求。对于未注册 mock 规则的请求接口,则正常发出 AJAX
请求。由于原生 mockjs
不是很方便使用,对于注册的 mock 规则的请求接口,由于没有发出 AJAX
请求,没法在控制台检测到其网络请求,形成调试困难。除此以外,原生 mockjs
对于以字符串注册 mock 规则的接口是严格匹配的,这致使了 URL 带查询字符串的请求没法匹配到,须要编写正则来实现匹配带查询字符串的 URL 。所以对原生 mockjs
进行改写,使其返回测试数据时先打印到控制台,方便调试,同时将注册 mock 规则的接口字符串转成能匹配查询字符串的正则。注册 mock 规则时应使用改写后的 mock
对象 。前端
node
命令行参数动态给 webpack
添加入口的代码以下,也可单独配一个配置文件来实现相同的效果// config 为 webpack 配置对象
// 获取命令行参数
const processArgvs = process.argv.slice(2)
// 判断是否有 mock 参数,有则在原入口的基础上带上 mock 工具与数据
if (processArgvs.includes('mock')) {
let entry = config.entry
if (Array.isArray(entry)) {
entry.push('./src/mock')
} else if (typeof entry === 'object') {
Object.keys(entry).forEach(name => {
if (Array.isArray(entry[name])) {
entry[name].psuh('./src/mock')
} else {
entry[name] = [entry[name], './src/mock']
}
})
} else {
config.entry = [entry, './src/mock']
}
}
// 以上代码加在启动 dev 环境的 webpack 配置文件中
// 经过 npm run dev mock 来启动前端 mock 环境
// npm run dev 启动前端联调环境
复制代码
src
|__ mock
|__ index.js // 入口文件,注册 mock 规则的文件所有 import 到这里
|__ utils
| |__ mock.js // 改写后的 mockjs,注册 mock 规则应使用该对象
| |__ formatOptions.js // 格式化注册 mock 时的回调函数的参数的函数,在 mock.js 中使用
|__ user.js // 按业务划分的 mock 规则注册文件
|__ business.js // 按业务划分的 mock 规则注册文件
复制代码
import Mock from 'mockjs'
import formatOptions from './formatOptions'
Mock._mock = Mock.mock
Mock.mock = function (url, method, resFunc) {
if (arguments.length === 1) {
return this._mock(url)
}
if (arguments.length === 2) {
console.error('Function Mock.mock require three params: url, method, resFunc!!!')
return
}
if (arguments.length === 3) {
let methods = ['get', 'post', 'put', 'delete']
if (!methods.includes(method.toLowerCase())) {
console.error('Function Mock.mock\'s second param should be get, post, put, delete!!!')
return
}
if (typeof resFunc !== 'function') {
console.error('Function Mock.mock\'s third param should be a function!!!')
return
}
}
// 将注册的 url 转成能匹配查询字符串的正则
if (typeof url === 'string') {
url = url.replace(/\//g, '\\/')
url += '(|\\?.*)$'
url = new RegExp(url)
} else if (!(url instanceof RegExp)) {
console.error('Function Mock.mock\'s first param should be a string or regexp!!!')
return
}
this._mock(url, method, function (options) {
// 格式化 options 对象
options = formatOptions(options)
let res = null
try {
res = resFunc(options)
} catch (err) {
res = err
}
// 将返回的测试数据打印到控制台
console.groupCollapsed(`%c${options.type.toLowerCase()} | ${options.url}`, 'color: green;')
console.log('%cparams: ', 'color: #38f')
console.log(options.params)
console.log('%cresponseData: ', 'color: #38f')
console.log(res)
console.groupEnd()
console.log('---------------')
return res
})
}
export default Mock
复制代码
// qs 用于序列化表单对象
import qs from 'qs'
export default function formatOptions (options) {
let { url, type, body } = options
let params = null
if (type === 'GET' || type === 'DELETE') {
let index = url.indexOf('?')
let paramsString = index > -1 ? url.slice(index + 1) : ''
if (paramsString !== '') {
params = qs.parse(paramsString)
}
} else {
params = {}
if (body instanceof FormData) {
for (let [key, value] of body.entries()) {
params[decodeURIComponent(key)] = decodeURIComponent(value)
}
} else {
try {
params = JSON.parse(body)
} catch (e) {
params = qs.parse(body)
}
}
}
if (params !== null && Object.keys(params).length === 0) {
params = null
}
return { url, type, params }
}
复制代码
Mock.mock(url, method, resFunc)
get
, post
, put
, delete
,忽略大小写{
url: String, // 请求的路径
type: String, // 请求的类型,GET, POST, PUT, DELETE
params: Object // 请求的参数,若是是 post 和 put 请求为 body 的内容,get 和 delete 为查询字符串解析出的对象,没有则为 null
}
复制代码
注:Mock.mock() 方法也支持传入一个模板来生成随机的测试数据,具体使用与原生 mockjs
一致,详见文档:vue
// 当前文件为 src/mock/user.js
import Mock from './utils/mock'
// 注册 post 请求
Mock.mock('/api/user/login', 'post', options => {
let { params } = options // options对象包含请求的 url,类型和携带的参数
if (params.username && params.password) {
return {
data: '',
code: 200,
message: '登陆成功'
}
} else {
return {
data: '',
code: 300,
message: '帐号或密码未输入'
}
}
})
// 注册 get 请求
Mock.mock('/api/user/logout', 'get', options => {
return {
data: '',
code: 200,
message: '注销成功'
}
})
// 注册带查询参数的 get 请求
Mock.mock('/api/user/query', 'get', options => {
return {
data: options.params,
code: 200,
message: 'ok'
}
})
复制代码
import './user'
console.log('%c前端 mock 环境启动成功', 'color: #38f;font-weight: bold')
复制代码
具体操做可查看 demonode
react-mock-demo npm start mock 开启 mock 环境react
vue-mock-demo npm run mock 开启 mock 环境webpack