vue中使用mockjs模擬后臺(tái)數(shù)據(jù)

前言:官網(wǎng)描述
1.前后端分離

2.不需要修改既有代碼质欲,就可以攔截 Ajax 請(qǐng)求绑嘹,返回模擬的響應(yīng)數(shù)據(jù)。

3.數(shù)據(jù)類型豐富

4.通過(guò)隨機(jī)數(shù)據(jù)年碘,模擬各種場(chǎng)景澈歉。
其實(shí)就是在后端接口開(kāi)發(fā)完成之前,我們前端可以用已有的接口文檔屿衅,在真實(shí)的請(qǐng)求上攔截ajax埃难,根據(jù)mockjs的mock數(shù)據(jù)的規(guī)則模擬真實(shí)接口返回的數(shù)據(jù),并將模擬數(shù)據(jù)返回參與相應(yīng)的數(shù)據(jù)交互處理涤久,這樣真正實(shí)現(xiàn)了前后臺(tái)的分離開(kāi)發(fā)涡尘。

與以往模擬的假數(shù)據(jù)不同,mockjs可以帶給我們的是:在后臺(tái)接口未開(kāi)發(fā)完成之前模擬數(shù)據(jù)并返回响迂,完成前臺(tái)的交互考抄;在后臺(tái)數(shù)據(jù)完成之后,你所做的只是去掉mockjs:停止攔截真實(shí)的ajax蔗彤,僅此而已川梅。
接下來(lái)就一步一步實(shí)現(xiàn)這個(gè)過(guò)程:

  1. 引入mockjs依賴
npm install mockjs --save-dev
  1. 建一個(gè)mock文件夾統(tǒng)一管理mock數(shù)據(jù)


    image.png

    3.在mock文件夾下建一個(gè)index.js(寫(xiě)關(guān)鍵代碼,攔截到我們前端發(fā)出的請(qǐng)求)

const Mock = require('mockjs')
// 解析地址欄參數(shù)的函數(shù)
const { param2Obj } = require('./utils')
// 導(dǎo)入模擬數(shù)據(jù)
const user = require('./user')
const role = require('./role')
const list = require('./list')

const mocks = [
  ...user,
  ...role,
  ...list
]

// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
function mockXHR() {
  // mock patch
  // https://github.com/nuysoft/Mock/issues/300
  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)
    }
  }

  // 批量注冊(cè)路由事件
  for (const i of mocks) {
    Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
  }
}

module.exports = {
  mocks,
  mockXHR
}
  1. mock文件夾下新建utile.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
}

/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

module.exports = {
  param2Obj,
  deepClone
}
  1. main.js文件中引入
// main.js 開(kāi)啟mock 服務(wù)
import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'development') {
  mockXHR()
}

6.下面封裝有兩種封裝使用方式
一然遏、
(1)mock文件夾中新增mock-server.js文件封裝請(qǐng)求
下載安裝 chokidar挑势、body-parser、chalk啦鸣、path
chokidar(監(jiān)聽(tīng)文件變化):

npm install --save-dev chokidar

body-parser(配合post 通過(guò)req.body獲取前端的請(qǐng)求數(shù)據(jù)):

npm install --save-dev express ejs body-parser

chalk(命令行顏色的插件):

npm install --save-dev chalk
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const Mock = require('mockjs')
// path模塊是node.js中處理路徑的核心模塊潮饱。可以很方便的處理關(guān)于文件路徑的問(wèn)題诫给。
// join() 將多個(gè)參數(shù)值合并成一個(gè)路徑
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) => {
  return {
    url: new RegExp(`${process.env.VUE_APP_BASE_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))
      }
    }
  })
}

(2)mockjs文件夾下新建幾個(gè)模擬數(shù)據(jù)文件(list.js)

// import Mock from 'mockjs'
const Mock = require('mockjs')
const List = []
const count = 50

 for (let i = 0; i < count; i++) {
   List.push(Mock.mock({
     id: '@increment',
     'date': '@date("yyyy-MM-dd")',
     'name': '@cname',
     'province': '@province',
     'city': '@city',
     'address': '@county(true)',
     'postcode': '@zip'
   }))
 }

module.exports = [
  {
    url: '/vue-element-admin/commonList/getList',
    type: 'get',
    response: config => {
      var { name, page, limit } = config.query
      var mocklist = List.filter(item => {
        if (name && item.name.indexOf(name) < 0) return false
        return true
      })
      page = Number(page)
      limit = Number(limit)
      var newDataList = mocklist.slice((page - 1) * limit, page * limit)

      return {
        code: 20000,
        data: {
          page: page,
          limit: limit,
          total: List.length,
          rows: newDataList
        }
      }
    }

  }
]

(3) 使用

getList(){
    axios.get('/vue-element-admin/commonList/getList',{    
        params:{
            page:1,
            limit: 20
        }
    }).then(function(res){
        this.goodsList = res.data.rows;
    }).catch(function (error) {
        console.log(error);
    });
}

二香拉、
(1)封裝請(qǐng)求方法

import axios from "axios";
import { Loading, Message } from "element-ui";
import store from "@/store";
// import { getToken } from "@/utils/auth";

// create an axios instance
const service = axios.create({
  // baseURL: "", 
  timeout: 10000, // request timeout
  // headers: {
  //   "Content-Type": "multipart/form-data",
  // },
});

let apiCallNo = 0;
let loadingInstance;

// request interceptor
// TODO 待優(yōu)化
service.interceptors.request.use(
  (config) => {
    if (config.data) {
      const { hideLoading, ...rest } = config.data;
      if (!hideLoading) {
        apiCallNo += 1;
        if (apiCallNo === 1) {
          loadingInstance = Loading.service();
        }
      }
      if (Object.keys(rest).length !== 0) {
        config.data = rest;
      } else if (typeof hideLoading === "boolean") {
        config.data = null;
      }
    } else {
      apiCallNo += 1;
      if (apiCallNo === 1) {
        loadingInstance = Loading.service();
      }
    }

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      // config.headers["X-Token"] = getToken();
    }
    return config;
  },
  (error) => {
    // do something with request error
    return Promise.reject(error);
  }
);

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  (response) => {
    apiCallNo -= 1;
    if (apiCallNo === 0) {
      loadingInstance.close();
    }
    const res = response.data;

    // 導(dǎo)出二進(jìn)制流數(shù)據(jù)
    if (res.type) {
      return res;
    }
    // 普通請(qǐng)求
    if (res.status !== 200) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000,
      });
      return Promise.reject(new Error(res.message || "Error"));
      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
      //   // to re-login
      //   MessageBox.confirm(
      //     "You have been logged out, you can cancel to stay on this page, or log in again",
      //     "Confirm logout",
      //     {
      //       confirmButtonText: "Re-Login",
      //       cancelButtonText: "Cancel",
      //       type: "warning",
      //     }
      //   ).then(() => {
      //     store.dispatch("user/resetToken").then(() => {
      //       location.reload();
      //     });
      //   });
      // }
    } else {
      return res.data;
    }
  },
  (error) => {
    console.log(error.response);
    apiCallNo -= 1;
    if (apiCallNo === 0) {
      loadingInstance.close();
    }
    Message({
      message: error.response?.data.message ?? "網(wǎng)絡(luò)異常,請(qǐng)重試", // TODO 是否要改成統(tǒng)一的提示中狂?
      type: "error",
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

export default service;

(2)新建一個(gè)文件專門(mén)封裝api

import request from "@/utils/request"; // 引入request方法
// 使用mock模擬后端數(shù)據(jù)
export function fetchResultTrend(params) {
  return request({
    url: "/mock/credit-evaluate-statistics/result/trend",
    params,
  });
}

(3)在vue文件中調(diào)用接口

async closerRateChange() {
      const res = await fetchResultTrend();
      console.log(res)
}

注:
npm / cnpm 下載時(shí)代表意義
node :自帶的包管理器
install :下載
--save :在package.json中存儲(chǔ)為線上需要的插件
--save-dev : 在package.json中存儲(chǔ)為線下需要的插件
express :基于node.js 平臺(tái)凫碌,快速、開(kāi)放胃榕、極簡(jiǎn)的文本開(kāi)發(fā)框架
ejs :通過(guò)數(shù)據(jù)和模板盛险,可以生成HTML標(biāo)記文本,可以同時(shí)運(yùn)行在客戶端和服務(wù)器端勋又,0 學(xué)習(xí)成本
body-parser :配合post 通過(guò)req.body獲取前端的請(qǐng)求數(shù)據(jù)
node .\app.js : 開(kāi)啟服務(wù)器苦掘,鏈接端口為listen設(shè)置的值
注意 :只要更改app.js ,都需要重新開(kāi)啟服務(wù)器

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楔壤,一起剝皮案震驚了整個(gè)濱河市鹤啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹲嚣,老刑警劉巖递瑰,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祟牲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抖部,警方通過(guò)查閱死者的電腦和手機(jī)说贝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慎颗,“玉大人乡恕,你說(shuō)我怎么就攤上這事』┳埽” “怎么了几颜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵倍试,是天一觀的道長(zhǎng)讯屈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)县习,這世上最難降的妖魔是什么涮母? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮躁愿,結(jié)果婚禮上叛本,老公的妹妹穿的比我還像新娘。我一直安慰自己彤钟,他們只是感情好来候,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著逸雹,像睡著了一般营搅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梆砸,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天转质,我揣著相機(jī)與錄音,去河邊找鬼帖世。 笑死休蟹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的日矫。 我是一名探鬼主播赂弓,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哪轿!你這毒婦竟也來(lái)了拣展?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缔逛,失蹤者是張志新(化名)和其女友劉穎备埃,沒(méi)想到半個(gè)月后姓惑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡按脚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年于毙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辅搬。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唯沮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出堪遂,到底是詐尸還是另有隱情介蛉,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布溶褪,位于F島的核電站币旧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏猿妈。R本人自食惡果不足惜吹菱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望彭则。 院中可真熱鬧鳍刷,春花似錦、人聲如沸俯抖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芬萍。三九已至尤揣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間担忧,已是汗流浹背芹缔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓶盛,地道東北人最欠。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惩猫,于是被迫代替她去往敵國(guó)和親芝硬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容