從零開始搭建一個mock服務

mock數據是一件頗有意義的事情,先後端能夠並行開發正是得益於mock生成的假數據,趁着有空擼了個輪子easy-config-mock,如下記錄實現思路以及最終實現的全部代碼技術細節javascript


關注點

記錄開發過程當中的關注點:html

如何實現mock數據的功能(技術選型)

使用mockjs去生成假數據(附:mock規則vue

使用express搭建服務(附:express官網java

如何集成到腳手架中,或者說工做流中

儘量設計得簡單,使其很容易集成到現有的腳手架或者工做流中,實際上easy-config-mock也作到了很容易集成,只需:node

const easyConfigMock = require('easy-config-mock')

new easyConfigMock({
  // 將配置文件路徑傳遞進去,服務會自動監聽文件變化並重啓服務
  path: path.resolve(__dirname, 'mock.config.js')
})
複製代碼
如何跟項目搭配使用

推薦將mock數據的配置文件放在項目的根目錄下,緣由在於mock數據跟業務是緊密聯繫的,丟在一塊兒容易查閱與維護,以下:webpack

+ vue-preject
-   node_modules
-   src
    mock.config.js   // mock數據的配置文件,名字僅作示例用
複製代碼
如何支持更復雜的場景

mockjs的功能很強大,能夠生成隨機假數據,但在業務場景很是複雜的狀況下這還不夠,有時爲了驗證顯示邏輯,指望是能夠定製mock接口的返回git

這是能夠作到的,得益於express的中間件,看一下easy-config-mock中的配置文件是怎麼寫的,更多說明github

// 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端口
  • 配置文件丟在項目裏面利於開發與維護

實現的具體技術細節

如下是實現該輪子須要的全部技術細節了,代碼僅簡要表達基本思想,詳情內容請看源碼web

如何監聽mock.config.js文件變化

使用chokidar模塊ajax

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-mock-webpack-plugin

其實直接用easy-config-mock就能夠了


參考連接


都看到這了,賞個贊👍唄~🙂😃😃😂😂😂源碼地址

本文完。

相關文章
相關標籤/搜索