關於mock數據,網上已經有不少成熟的方案,可是我我的在開發的時候,我習慣性,凡事從簡,我拿到接口文檔的時候,我首先會選擇使用這個本地mock方法,好處就是可控,適合在開發初期的時候,固然還有個好處就是若是到聯調階段的時候,也不用在改變接口地址,只要把Mock數據刪掉,就能訪問到真實接口。javascript
主要流程圖爲如下: html
算了別看了,反正如今看也看不懂啥意思。直接上代碼咱們但願他是做爲一個對象,單獨放在一個js文件裏面,而後咱們的mock方法能夠從裏面提取數據 ,咱們就建立一個js文件名爲:test_data.js
前端
裏面有一個名爲query:user
的接口,這個接口裏面有個mock數據,咱們但願當咱們請求xxx://query:user
接口的時候,可以拉取到這個mock數據,當須要聯調真實接口的時候,咱們只需把這幾行假數據去掉,即可獲取到真實接口傳過來的真實信息vue
const data = {
"query:user": {
"code": "S_OK",
"record": [
{
"id": "132",
"name": "silan",
"job": "前端開發工程師",
"workTime": "3年"
}
]
},
}
複製代碼
按照慣例,咱們須要一個構造函數,它有一個參數,用來接收mock數據傳進去的接口地址,這個參數應該是個數組java
由於你可能要mock不少接口地址的數據,所以爲了讓別人以爲你是個js老手,同時也爲了代碼茁壯性,咱們須要聲明一個類class
,後端
class MockRequest{
constructor(rules) {
this.rules = rules // 獲取假數據的接口地址
}
}
複製代碼
接下來就是比較複雜的部分了,咱們須要定義一個原型方法,在它內部須要完成三件事:api
暫且將其命名爲create
:數組
class MockRequest{
constructor(rules) {
this.rules = rules // 獲取假數據的接口地址
}
create(){
// 匹配Url方法
function matchRules(url) {
var r = self.rules;
var result = [];
for (var i = 0; i < r.length; i++) {
if (r[i] && url.indexOf(r[i].url) >= 0) {
result.push(r[i]);
}
}
if (result.length > 0) {
return result[0]
}
}
}
}
複製代碼
接着補上比較複雜的替代對象服務器
class MockRequest {
constructor(rules) {
this.rules = rules
}
create() {
let self = this;
// 匹配url
function matchRules(url) {
//...
}
// returnObj 擁有open 和send方法
let returnObj = {
// onreadystatechange: null,
open: function (method, url, async) {
this.url = url;
this.xhr.open(method, url, async);
},
send: function (data) {
// this指向MockRequestContructor
let self = this;
//獲取匹配到的url地址
let rule = matchRules(self.url);
if (rule) {
// 若是匹配地址爲mock的地址則把mock數據傳給前端
setTimeout(function () {
self.readyState = 4;
self.status = 200;
//獲取返回的mock數據的responseText
if (typeof rule.responseText == "string") {
self.responseText = rule.responseText;
} else if (typeof rule.responseText == "object") {
self.responseText = JSON.stringify(rule.responseText);
}
//觸發用戶在前端設置的onreadyStatechange方法,前端就能獲取到mock數據
self.onreadystatechange && self.onreadystatechange();
}, 100);
} else {
// 若是不是mock地址,則發生原生請求
self.xhr.send(data);
}
}
}
// 講XMLHttpRequest構造函數賦值給_XMLHttpRequest
let _XMLHttpRequest = window.XMLHttpRequest
function MockRequestContructor() {
this.xhr = new _XMLHttpRequest();
// 把XMLHttpRequest實例賦值到this.xhr,爲的是方便Object.defineProperty操做
}
// 此時的MockRequestContr是個構造函數,把returnObj裏面的屬性和方法綁定到他的原型上
MockRequestContructor.prototype = returnObj;
//這時候前端調用的 new XMLHttpRequest
//已經被咱們成功改寫,調用的實際上是MockRequestContructor這個構造函數
window.XMLHttpRequest = MockRequestContructor;
}
}
複製代碼
走到這一步,其實已經完成了獲取mock數據的操做,當咱們拉取一個已經設置好mock數據的url地址的時候,返回的是mock數據,而不會發送任何請求都目標服務器框架
剩下來的就只有實現
「當不是mock數據地址的時候,發送請求到目標服務器獲取真實數據」 功能
咱們都知道,獲取responseText
只能在onreadystatechange
中獲取,而目前原來的XMLHttpRequest
只能獲取到mock數據,所以咱們要經過Object.defineProperty
對其進行改寫,當前端在設置 xhr.onreadystatechange=function(){xxxx}
的時候就觸發Object.defineProperty
,咱們就能夠把獲取到的真實數據手動傳入到onreadystatechange
裏面
Object.defineProperty(returnObj, "onreadystatechange", {
get: function () {
return this.xhr.onreadystatechange;
},
set: function (func) {
var obj = this; // 這裏的obj指的是 MockRequestContructor
console.log(obj,func)
// 若是obj.xhr.onreadystatechange有變化,說明是真實請求地址
obj.xhr.onreadystatechange = function (arg) {
// this指向http請求真實數據
// returnObj就是獲取到的返回對象,把真實的值替換回去
returnObj.readyState =this.readyState;
returnObj.status =this.status;
returnObj.responseText =this.responseText;
returnObj.responseXML =this.responseXML;
func(arg);
};
}
})
複製代碼
完整代碼
class MockRequest {
constructor(rules) {
this.rules = rules
}
create() {
let self = this;
// 匹配url
function matchRules(url) {
let r = self.rules;
let result = [];
for (let i = 0; i < r.length; i++) {
if (r[i] && url.indexOf(r[i].url) >= 0) {
result.push(r[i]);
}
}
if (result.length > 0) {
return result[0]
}
}
// returnObj 擁有open 和send方法
let returnObj = {
// onreadystatechange: null,
open: function (method, url, async) {
this.url = url;
this.xhr.open(method, url, async);
},
send: function (data) {
// this指向MockRequestContructor
let self = this;
//獲取匹配到的url地址
let rule = matchRules(self.url);
if (rule) {
// 若是匹配地址爲mock的地址則把mock數據傳給前端
setTimeout(function () {
self.readyState = 4;
self.status = 200;
//獲取返回的mock數據的responseText
if (typeof rule.responseText == "string") {
self.responseText = rule.responseText;
} else if (typeof rule.responseText == "object") {
self.responseText = JSON.stringify(rule.responseText);
}
//觸發用戶在前端設置的onreadyStatechange方法,前端就能獲取到mock數據
self.onreadystatechange && self.onreadystatechange();
}, 100);
} else {
// 若是不是mock地址,則發生原生請求
self.xhr.send(data);
}
}
}
// 監聽onreadystatechange變化
Object.defineProperty(returnObj, "onreadystatechange", {
get: function () {
return this.xhr.onreadystatechange;
},
set: function (func) {
var obj = this; // 這裏的obj指的是 MockRequestContructor
// 若是obj.xhr.onreadystatechange有變化,說明是真實請求地址
obj.xhr.onreadystatechange = function (arg) {
// this指向http請求真實數據
// returnObj就是獲取到的返回對象,把真實的值替換回去
returnObj.readyState = this.readyState;
returnObj.status = this.status;
returnObj.responseText = this.responseText;
returnObj.responseXML = this.responseXML;
func(arg);
};
}
})
// 講XMLHttpRequest構造函數賦值給_XMLHttpRequest
let _XMLHttpRequest = window.XMLHttpRequest
function MockRequestContructor() {
this.xhr = new _XMLHttpRequest();
// 把XMLHttpRequest實例賦值到this.xhr,爲的是方便Object.defineProperty操做
}
// 此時的MockRequestContr是個構造函數,把returnObj裏面的屬性和方法綁定到他的原型上
MockRequestContructor.prototype = returnObj;
//這時候前端調用的 new XMLHttpRequest
//已經被咱們成功改寫,調用的實際上是MockRequestContructor這個構造函數
window.XMLHttpRequest = MockRequestContructor;
}
}
複製代碼
只須要兩步
在放置mock數據的test_data.js
中配置好mock數據,而後new MockRequest
const data = {
"query:user": {
"code": "S_OK",
"record": [
{
"id": "132",
"name": "silan",
"job": "前端開發工程師",
"workTime": "3年"
}
]
},
}
var rules = [];
for (var key in data) {
//轉爲數組
rules.push({
url: key,
responseText: data[key]
})
}
// 使用mock
new MockRequest(rules).create()
複製代碼
<html lang="en">
<head>
<meta charset="UTF-8">
<!--引入文件-->
<!--mock方法-->
<script src="./mockrequest.js"></script>
<!--mock數據-->
<script src="./test_data.js"></script>
<title>mock</title>
</head>
<body>
<script> var xhr = new XMLHttpRequest(); // xhr.open('GET', 'https://www.apiopen.top/journalismApi') //這裏是真實接口 xhr.open('GET', 'query:user') // mock數據接口 xhr.send() xhr.onreadystatechange = function (res) { if (xhr.readyState === 4 && xhr.status === 200) { console.log(JSON.parse(xhr.responseText)) } } </script>
</body>
</html>
複製代碼
能夠成功獲取測試數據
而後咱們再試下真實數據接口,把// xhr.open('GET', 'https://www.apiopen.top/journalismApi')
這行取消註釋,而且註釋掉mock數據請求方法
也可以成功獲取
在vue
獲取其餘框架項目中使用方法也是同樣的,引入兩個js文件就好了,當到聯調階段的時候,就把聯調的那個接口地址註釋掉就好了,便能獲取到真實的數據
本文只是提供給各位一個Object.defineProperty
的擴展使用思路,正如掘金console所言
[不要吹滅你的靈感和你的想象力; 不要成爲你的模型的奴隸]
謝謝各位