項目開發過程當中,一般先定義接口格式,先後端並行開發。若是前端先開發完成,由於接口還沒有實現,只能在代碼中寫一些測試數據來測試頁面效果。這致使了測試數據侵入業務代碼,後期上線時還得刪去。如今的中大型應用一般會使用 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