Mock
mock 介紹
目的
方便生成隨機數據,攔截Ajax請求。
偶爾會寫一些前端的項目,參考一些比較知名的UI框架,裏面很多例子都會把數據直接寫在裏面,作爲參考,因此很多項目裏面也會這樣手寫數據,直接寫死在前端,將代碼與mock數據緊密結合在一起,每次需要修改數據,都需要直接改動到代碼層,前端本身改動就非常大,如此頻繁的修改代碼層面顯然不是很好的實踐。
同時手寫的數據,美觀與真實性差異就看作者水平了。
優點
- 非常簡單強大,入手快
- 攔截api請求,mock出真實的前後端交互情況
- 更改mock數據方便,與業務代碼分離,頻繁修改也不會有太大的危險
- 在後端接口準備好前,通過mock接口來模擬與後臺的交互,同時調整數據結構。
- 開發過程中,mock數據也可以與後端提前討論,引導後臺的數據結構更加合理。
- 後臺開發完以後,可以一次性切換接口
缺點
- 靈活性不夠,無法mock後臺一些異常處理返回
- Build成靜態文件以後線上無法使用(或許我沒有找對方法)
comment
針對第一個問題,忽然想到一種方案,或許你可以在同一個接口裏面,mock不同的response,然後採用隨機函數,隨機的返回各種異常response,這樣可以測試前端應對各種後臺error作出的處理是否符合預期。
安裝
cd myProject
#僅安裝到本地開發環境
npm install mockjs --save-dev
mock 實現
src
│
└───api //api文件夾,所有的接口都到單獨抽出來,放在該目錄下
│ index.js //入口函數,對外暴露
│ search.js //爲每一類接口單獨創建的文件,一類接口統一放在一個目錄下
│
└───mock
index.js //所有的mock api都會寫在下面,如果需要,也可以拆分
mock/index.js
/**
* data mock refer to http://mockjs.com
*
*/
const Mock = require('mockjs');
const util = require('./util');
module.exports = function (app) {
app.get('/mock/v1/search', function (rep, res) {
let result = {
"data|24": [{
"date": "@date('yyyy-MM-dd')",
"logNum": "@natural(60, 1000)"
}
]
};
//util.wrapResultSuccess 包裝了response返回,規範統一
res.send(util.wrapResultSuccess(Mock.mock(result).data));
});
}
vue.config.js
//vue cli3 配置文件
devServer: {
port: port,
open: true,
host: '127.0.0.1',
https: false,
hotOnly: false,
overlay: {
warnings: false,
errors: true
},
#根據當前環境,把整個mock目錄加載到項目中,每次修改mock文件需要重啓
before: process.env.ENV == 'mock' ? require('./src/mock'):null,
},
api/index.js
/**
* api interface
*/
import search from '@/api/search'
export default {
search
}
api/search.js
/**
* article模塊接口列表
*/
import axios from '@/plugins/request';
import trend from "@/plugins/handle"
const search = {
statusPing () {
return axios.get(`/status_ping`);
},
//search
search (body) {
return axios.get(`v1/search`, body);
},
export default search;
utils/request.js
把axiso做了一層封裝,每一次前端請求都會把x-request-id帶給後臺,後臺打印日誌的時候,都會打印這個id,方便日後追蹤問題,同事針對一些後臺的error,可以做一些統一的處理。
import router from '@/pages/login/router';
const xss = require("xss");
let instance = axios.create({
timeout: 1000 * 60
});
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
function getRandomTraceId() {
let characters = 'abcdefghijklmnopqrstuvwxyz1234567890';
let result = '';
let idLength = 32;
try {
for (let index = 0; index < idLength; index++) {
result += characters[parseInt(Math.random() * characters.length, 10)];
}
} catch (error) {
result = 'getRandomTraceIdError';
}
return result;
}
/**
* request interceptor
* Before each request, if token exists, carry it in the request header
*/
instance.interceptors.request.use(
config => {
// console.log(JSON.stringify(config));
//add trace id for request
config.headers['x-request-id'] = getRandomTraceId();
//xss filter request url
config.url = xss(config.url);
// 登錄流程控制中,根據本地是否存在token判斷用戶的登錄情況
// 但是即使token存在,也有可能token是過期的,所以在每次的請求頭中攜帶token
// 後臺根據攜帶的token判斷用戶的登錄情況,並返回給我們對應的狀態碼
// 而後我們可以在響應攔截器中,根據狀態碼進行一些統一的操作。
// const token = store.state.token;
// token && (config.headers.Authorization = token);
return config;
},
error => Promise.error(error))
instance.interceptors.response.use(
res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
error => {
const {response} = error;
if (response) {
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 處理斷網的情況
// eg:請求超時或斷網時,更新state的network狀態
// network狀態在app.vue中控制着一個全局的斷網提示組件的顯示隱藏
// 關於斷網組件中的刷新重新獲取數據,會在斷網組件中說明
if (!window.navigator.onLine) {
// store.commit('changeNetwork', false);
} else {
return Promise.reject(error);
}
}
});
export default instance;
—main.js
直接把api類掛載到vue上,這樣方便使用,也可以直接到使用類裏面去引入
import api from '@/api'
Vue.prototype.$api = api;
window.APP=new Vue({
router,
i18n,
api,
store,
render: h => h(App)
}).$mount('#app')
how to use
async getData(){
let response = await this.$api.search.search(request);
if (response.status == 200 && response.data.data) {
console.log(`response.data.data = &{response.data.data}`);
}
}