mock資料是一件很有意義的事情,前後端可以並行開發正是得益於mock生成的假資料,趁著有空擼了個輪子easy-config-mock,以下記錄實現思路以及最終實現的所有程式碼技術細節
關注點
記錄開發過程中的關注點:
如何實現mock資料的功能(技術選型)
使用mockjs
去生成假資料(附:mock規則)
使用express
搭建服務(附:express官網)
如何整合到腳手架中,或者說工作流中
儘可能設計得簡單,使其很容易整合到現有的腳手架或者工作流中,實際上easy-config-mock
也做到了很容易整合,只需:
const easyConfigMock = require('easy-config-mock')
new easyConfigMock({
// 將配置檔案路徑傳遞進去,服務會自動監聽檔案變化並重啟服務
path: path.resolve(__dirname, 'mock.config.js')
})
複製程式碼
如何跟專案搭配使用
推薦將mock資料的配置檔案放在專案的根目錄下,原因在於mock資料跟業務是緊密聯絡的,丟在一起容易查閱與維護,如下:
+ vue-preject
- node_modules
- src
mock.config.js // mock資料的配置檔案,名字僅做示例用
複製程式碼
如何支援更復雜的場景
mockjs
的功能很強大,可以生成隨機假資料,但在業務場景非常複雜的情況下這還不夠,有時為了驗證顯示邏輯,期望是可以定製mock介面的返回
這是可以做到的,得益於express的中介軟體,看一下easy-config-mock
中的配置檔案是怎麼寫的,更多說明
// mock.config.js
module.exports = {
// common選項不是必須的,可以不用有該選項,內建的配置如下,當然你也可以更改
common: {
// mock服務的預設埠,如果埠被佔用,會自動換一個
port: 8018,
// 如果你想看一下ajax的loading效果,該配置項可以設定介面的返回延遲
timeout: 500,
// 如果你想看一下介面請求失敗的效果,將rate設定成0就可以了,rate取值範圍0~1,代表成功的概率
rate: 1,
// 預設是true,自動開啟mock服務,當然你也可以通過將其設定為false,關閉掉mock服務
mock: true
},
// 普通的api...
'/pkApi/getList': {
code: 0,
'data|5': [{
'uid|1000-99999': 999,
'name': '@cname'
}],
result: true
},
// 中介軟體api(標準的express中介軟體),這裡你可以書寫介面返回邏輯
['/pkApi/getOther'] (req, res, next) {
const id = req.query.id
req.myData = { // 重要! 將返回資料掛載在req.myData
0: {
code: 0,
'test|1-100': 100
},
1: {
code: 1,
'number|+1': 202
},
2:{
code: 2,
'name': '@cname'
}
}[id]
next() // 最後不要忘記手動呼叫一下next,不然介面就暫停處理了!
}
}
複製程式碼
easy-config-mock的優點
- 很容易整合到腳手架或者工作流中,並且可以自動重啟服務
- 支援自定義中介軟體,以滿足更為複雜的業務場景
- 基本不會侵入業務程式碼,只需要將介面的請求字首改成
http://127.0.0.1:mock埠
- 配置檔案丟在專案裡面利於開發與維護
實現的具體技術細節
以下是實現該輪子需要的所有技術細節了,程式碼僅簡要表達基本思想,詳情內容請看原始碼
如何監聽mock.config.js
檔案變化
使用chokidar
模組
const chokidar = require('chokidar')
chokidar.watch(somepath, {
persistent: true
}).on('change', _ => {
// file change...
// do some logic...
})
複製程式碼
如何實現服務的自動重啟
fork
子程式去啟動express
服務,當配置檔案發生變化的時候,殺掉子程式並重啟服務
const childProcess = require('child_process')
let child
// 使用子程式啟動express服務
child = childProcess.fork('./server.js', [], {
encoding: 'utf8',
execArgv: process.execArgv
})
chokidar.watch(somepath, {
persistent: true
}).on('change', _ => {
// 檔案發生變化後殺死子程式並重啟服務
child.kill('SIGKILL')
child = childProcess.fork('./server.js', [], {
encoding: 'utf8',
execArgv: process.execArgv
})
})
複製程式碼
子程式如何讀取到配置檔案資料
程式給父程式傳遞的資料子程式是不知道的,可以利用父子程式之間的通訊,可以參考child_process中子程式與父程式之間的通訊與斷開連線
// 給子程式傳遞資料
child.send({
path: path
...
})
// 子程式接收到資料
process.on('message', ({ path, ... }) => {
delete require.cache[path]
// 這裡,拿到了mock資料的配置項
const options = require(path)
})
複製程式碼
require是有快取的,需要先刪除require的快取,再去重新獲取配置檔案的資料
如何去模擬jsonp的請求
首先得知道該請求是否是jsonp,檢測請求連結是否帶有callback引數
let dataType
app.use((req, res, next) => {
dataType = req.query.callback ? 'jsonp' : 'json'
next()
})
複製程式碼
如何延遲介面的返回
有時,我們編寫了loading的效果並想驗證一下
const delayRes = (time) => (req, res, next) => {
setTimeout(function() { next() }, time)
}
// 給介面增加1秒延遲
app.use(delayRes(1000))
複製程式碼
如何讓介面返回失敗
有時,我們想看下斷網或者伺服器出錯時的效果
const successRate = (rate) => (req, res, next) => {
if (rate > Math.random()) return next()
return next(500)
}
// 100%返回500錯誤
app.use(successRate(0))
app.use((err, req, res, next) => {
res.status(500).json({ status: 0, code: 500, msg: 'Server Error' })
})
複製程式碼
如何允許跨域
訪問的非jsonp的mock介面是跨域請求(協議,域名,埠三者相同才為同域)
,跨域請求是禁止的,會報錯,這裡需要設定為允許跨域
const crossDomain = () => (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
if (req.method === 'OPTIONS') res.status(200) // 讓OPTIONS快速返回
next();
}
app.use(crossDomain())
複製程式碼
最終:路由建立
const { mock } = require('mockjs')
// options是配置檔案裡面的api資訊
Object.keys(options).forEach(path => {
const data = options[path]
// 如果是自定義中間
if (typeof data === 'function') app.use(data)
app.use(path, (req, res, next) => {
// req中帶有myData的話說明是自定義中介軟體,否則是普通的mock api
const rsp = req.myData ? mock(req.myData) : mock(data)
res.status(200)[dataType](rsp)
})
})
複製程式碼
補充:easy-config-mock的webpack版外掛
其實直接用easy-config-mock
就可以了
參考連結
都看到這了,賞個贊?唄~??????原始碼地址
本文完。