我與我周旋久,寧作我
在前端開發中,使用 Mock
方案可以幫助開發人員在不依賴於後端介面的情況下進行開發和除錯。Mock
資料可以模擬後端介面的返回結果,使得前端開發可以獨立進行,不受後端介面的限制。以下是幾種常見的前端Mock
方案:
手動編寫Mock資料
手動編寫 Mock
資料是一種簡單而直接的前端Mock
方案。你可以手動建立 JSON
或 JavaScript
物件來模擬後端介面的返回資料。下面是一個示例,展示如何手動編寫 Mock
資料:
// 模擬使用者列表介面的Mock資料
const mockUsers = [
{ id: 1, name: "John Doe", email: "john@example.com" },
{ id: 2, name: "Jane Smith", email: "jane@example.com" },
{ id: 3, name: "Bob Johnson", email: "bob@example.com" }
];
const sleep = (wait) => new Promise((resolve) => setTimeout(resolve, wait));
// 模擬獲取使用者列表的介面
axios.get("/api/users").then(() => sleep(500)).then(() => mockUsers);
這種方式適用於簡單的場景,但對於複雜的介面或大量資料的情況下,手動編寫 Mock
資料可能會變得繁瑣。
Mock.js
Mock.js
是一個前端模擬資料生成器,可以幫助開發人員快速生成隨機資料,並攔截 Ajax
請求。它提供了豐富的資料模板語法和模擬請求攔截功能,可以模擬後端介面的返回結果。Mock.js
可以輕鬆整合到前端專案中,並與其他前端框架(如Vue
、React
等)配合使用。
在引入 Mock.js
庫時,會執行初始化操作。這包括建立全域性的 Mock
物件和生成 MockXMLHttpRequest
建構函式來完整地模擬原生 XHR(XMLHttpRequest)
的行為。然後,當呼叫 Mock.mock()
方法時,Mock.js
開始解析資料模板,並生成模板資料,儲存到快取變數中。
為了能夠攔截和處理前端傳送的 AJAX
請求,Mock.js
將全域性的 window.XMLHttpRequest
物件替換為 MockXMLHttpRequest
。由於許多前端框架和庫(如 axios
)底層也使用了 XMLHttpRequest
傳送網路請求,現在 XMLHttpRequest
被替換成了 MockXMLHttpRequest
,從而使得 Mock.js
能夠攔截這些請求。
當使用 axios
或其他基於 XMLHttpRequest
的網路請求庫傳送請求時,如果有匹配的資料模板,MockXMLHttpRequest
將會返回模擬資料,並將請求和響應狀態同步給 axios
(比如 axios
中監聽了 readystatechange
事件,MockXMLHttpRequest
模擬響應後需要觸發 readystatechange
)。這樣,前端開發人員可以在開發和除錯過程中使用模擬資料,而無需實際訪問後端介面。
如果沒有匹配的資料模板,MockXMLHttpRequest
內部會呼叫原生 XMLHttpRequest
傳送請求,並將請求和響應狀態同步給 axios
,以便實際的網路請求能夠正常進行。
透過以上流程,Mock.js
可以方便地攔截和模擬前端的網路請求,提供模擬資料,以支援前端開發和除錯的需求。
當在 React
中使用 Mock.js
,你可以透過以下示例來模擬網路請求和使用模擬資料:
import React from 'react';
import Mock from 'mockjs';
import axios from 'axios';
class MyComponent extends React.Component {
componentDidMount() {
// 定義資料模板和攔截規則
Mock.mock('/api/users', 'get', {
'list|5': [{
'id|+1': 1,
'name': '@cname',
'age|18-60': 1
}]
});
// 攔截請求並延遲響應
Mock.setup({
timeout: '1000-3000'
});
// 使用模擬資料
axios.get('/api/users')
.then(response => {
console.log(response.data); // 模擬資料
// 處理模擬資料邏輯
})
.catch(error => {
// 處理錯誤邏輯
});
}
render() {
// 元件渲染邏輯
return (
// JSX 介面程式碼
);
}
}
在上面的示例中,我們在 componentDidMount()
生命週期方法中定義了一個 GET
請求的資料模板和攔截規則,並使用模擬資料來處理網路請求。當元件掛載後,Mock.js
會攔截 /api/users
的 GET
請求並返回模擬資料。在使用 axios
發起網路請求後,可以透過 then
回撥處理返回的模擬資料。
雖然 Mock.js
是一個強大的工具,但也存在一些侷限性,包括:
- 無法模擬複雜的後端邏輯:
Mock.js
主要用於模擬介面返回的資料,但無法完全模擬後端的複雜邏輯和業務流程(增刪改)。對於需要與後端進行深度互動的場景,Mock.js
可能無法提供準確的模擬資料和行為。 - 需要手動編寫資料模板和攔截規則:使用
Mock.js
需要手動編寫資料模板和攔截規則,這對於複雜的資料結構和介面規範可能需要花費較多的時間和精力。對於大型專案或頻繁變動的介面,維護這些規則可能會帶來一定的工作量。 - 對於使用非
XMLHttpRequest
的網路請求庫支援有限:Mock.js
主要攔截和模擬XMLHttpRequest
的請求,對於使用其他網路請求庫(如fetch
)的專案,Mock.js
的支援可能相對有限。需要額外的配置和適配才能與這些庫進行整合。
axios-mock-adapter
Axios
例項的 defaults.adapter
屬性用於配置介面卡,預設情況下,Axios
使用 xhrAdapter
作為介面卡,基於瀏覽器的 XMLHttpRequest
物件傳送請求。而在 Node.js
環境中,Axios
使用 httpAdapter
作為介面卡,利用 Node.js
的 http
模組傳送請求。
axios-mock-adapter
是一個與 Axios
配合使用的外掛。它透過例項化一個 mockAdapter
物件,並將其指定給 Axios
例項的 defaults.adapter
屬性,從而將其作為介面卡。
當使用 Axios
傳送請求時,Axios
例項會根據 defaults.adapter
配置的介面卡來處理請求。在正常情況下,請求會被髮送到伺服器,並返回伺服器的響應。然而,當 defaults.adapter
被設定為 mockAdapter
時,axios-mock-adapter
將攔截匹配的請求,並返回預先定義的模擬資料,而不會實際傳送請求到伺服器。
function adapter() {
return function (config) {
var mockAdapter = this;
return new Promise(function (resolve, reject) {
handleRequest(mockAdapter, resolve, reject, config);
});
}.bind(this);
}
function MockAdapter(axiosInstance, options) {
reset.call(this);
if (axiosInstance) {
this.axiosInstance = axiosInstance;
// Clone the axios instance to remove interceptors
// this is used for the passThrough mode with axios > 1.2
this.axiosInstanceWithoutInterceptors = axiosInstance.create
? axiosInstance.create()
: undefined;
this.originalAdapter = axiosInstance.defaults.adapter;
this.delayResponse =
options && options.delayResponse > 0 ? options.delayResponse : null;
this.onNoMatch = (options && options.onNoMatch) || null;
// 指定介面卡
axiosInstance.defaults.adapter = this.adapter.call(this);
} else {
throw new Error("Please provide an instance of axios to mock");
}
}
function handleRequest(mockAdapter, resolve, reject, config) {
// code...
var handler = utils.findHandler(
mockAdapter.handlers,
config.method,
url,
config.data,
config.params,
(config.headers && config.headers.constructor.name === 'AxiosHeaders')
? Object.assign({}, config.headers)
: config.headers,
config.baseURL
);
if (handler) {
// 有匹配路由,返回模擬資料
} else {
// 沒有匹配路由,走實際請求
}
}
以下是在 React
中使用 axios-mock-adapter
示例:
import React, { useEffect } from 'react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
const YourComponent = () => {
useEffect(() => {
const mock = new MockAdapter(axios);
// 在這裡設定模擬請求的規則和響應
mock.onGet('/api/endpoint').reply(200, { data: 'mocked data' });
mock.onPost('/api/endpoint').reply(200, { data: 'mocked data' });
// 使用 Axios 傳送請求
axios.get('/api/endpoint').then((response) => {
console.log(response.data);
});
}, []);
return <div>...</div>;
};
axios-mock-adapter
是一個用於模擬和測試 Axios
請求的強大工具,然而,使用 axios-mock-adapter
也有一些缺點,主要缺點就是 axios-mock-adapter
只能與 axios
搭配使用,不能支援複雜的業務場景(增刪改)。
webpack-dev-mock
mock-dev-server
是一個基於 Express
的中介軟體,它允許你自定義中介軟體來攔截請求,並根據路由匹配返回模擬資料,否則將繼續傳送實際請求。
以下是 mock-dev-server
關鍵核心程式碼:
app.use(function (req, res, next) {
// 請求攔截,匹配路由
var match = mockConfig.length && (0, matchPath_1.default)(req, mockConfig);
if (match) {
debug("mock matched: [" + match.method + "] " + match.path);
// 如果有匹配路由,則執行自定義中介軟體
return match.handler(req, res, next);
}
else {
// 如果沒有匹配路由,則執行後續中介軟體行為,傳送實際請求
return next();
}
});
在使用 React
進行開發時,你可以透過 webpack-dev-server
搭建本地開發伺服器,並結合 mock-dev-server
中介軟體來實現請求攔截和模擬資料返回的功能。webpack-dev-server
提供了一個名為 before
的回撥函式,會自動查詢專案中是否存在 setupProxy.js
檔案,並將其作為配置檔案來新增中介軟體。
before(app, server) {
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
// middlewares before `redirectServedPath` otherwise will not have any effect
// This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server));
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
// 自動查詢專案中是否存在 setupProxy.js 檔案
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
},
以下是 React
專案使用 mock-dev-server
模擬資料的示例:
(1)配置模擬資料
// mock/index.js
module.exports = {
// 支援引數
'POST /api/users/:id': (req, res) => {
const { id } = req.params;
res.send({ id: id });
},
}
(2)在 setupProxy.js
新增自定義中介軟體
const proxy = require('http-proxy-middleware');
const mockDevServer = require('mock-dev-server');
module.exports = function (app) {
mockDevServer(app); // 預設讀取專案目錄下的 mock/index.js 檔案生成,會配置對應的介面。
// 代理到開發環境
app.use(proxy([
"/api/xxx"
], {
target: 'https://example.com/',
changeOrigin: true,
}));
};
mock-dev-server
是一個在前端開發中常用的工具,用於實現請求攔截和模擬資料返回的功能。它具有以下優點和缺點:
優點:
- 靈活性:
mock-dev-server
允許開發人員自定義中介軟體來攔截請求並返回模擬資料,因此具有很高的靈活性。你可以根據需要定義路由和對應的模擬資料,滿足專案的特定需求。 - 易於整合:
mock-dev-server
基於Express
,與常見的前端開發工具和框架整合較為容易。你可以將其作為webpack-dev-server
的中介軟體來使用,或者與其他構建工具或框架搭配使用,如create-react-app
、Vue CLI
等。
缺點:
- 無法模擬複雜的業務場景:儘管
mock-dev-server
可以模擬後端介面的響應,但它無法模擬後端的業務邏輯。對於一些複雜的業務場景或後端計算,可能無法完全模擬真實的後端行為。
json-server
json-server
是一個基於 Node.js
的工具,可以根據提供的 JSON
檔案快速搭建一個 RESTful API
的臨時伺服器。開發人員可以將需要模擬的資料儲存在 JSON
檔案中,然後使用 json-server
啟動一個本地伺服器,前端透過傳送 HTTP
請求來獲取資料。這個方案簡單易用,適用於快速搭建臨時的 Mock
伺服器。
json-server
使用 Express
來搭建服務和管理路由。當你提供了路由配置檔案時,json-server
將按照該檔案中定義的規則來生成路由。這樣你可以更靈活地定義資源的路由和操作方式。如果沒有提供路由配置檔案,json-server
會根據資料檔案的結構自動生成預設的路由,包括對資料的增刪改查操作。
此外,json-server
還支援自定義中介軟體。你可以新增自己的中介軟體函式來實現額外的功能,例如身份驗證、日誌記錄等。這樣你可以在 API
請求的不同階段進行自定義處理。
另一個重要的特性是,當資料檔案發生變更時,json-server
會自動重新啟動服務。這意味著你可以輕鬆地修改和更新資料檔案,而無需手動停止和重新啟動伺服器,從而提高開發效率。
以下是一個使用 json-server
的簡單示例:
首先,安裝所需的依賴:
$ yarn add json-server global
接下來,建立一個名為 db.json
的資料檔案,作為 json-server
的資料來源。在該檔案中,可以定義一些示例資料,例如:
{
"posts": [
{ "id": 1, "title": "Post 1", "content": "This is the content of Post 1" },
{ "id": 2, "title": "Post 2", "content": "This is the content of Post 2" }
]
}
然後,建立一個名為 api.js
的檔案,以使用 axios
庫來傳送 HTTP
請求。示例程式碼如下:
import axios from 'axios';
const api = axios.create({
baseURL: '/',
});
export const getPosts = () => api.get('/posts');
export const getPost = (id) => api.get(`/posts/${id}`);
export const createPost = (data) => api.post('/posts', data);
export const updatePost = (id, data) => api.put(`/posts/${id}`, data);
export const deletePost = (id) => api.delete(`/posts/${id}`);
const apis = {
getPosts,
getPost,
createPost,
updatePost,
deletePost,
};
export default apis;
接下來,建立一個名為 Posts.js
的元件,用於展示帖子列表。該元件將使用 api.js
中定義的函式來獲取帖子資料。示例程式碼如下:
import React, { useEffect, useState } from 'react';
import api from './api';
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await api.getPosts();
setPosts(response.data);
};
fetchData();
}, []);
return (
<div>
<h2>Posts</h2>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</li>
))}
</ul>
</div>
);
};
export default Posts;
現在,在終端中執行以下命令啟動 json-server
:
$ json-server --watch db.json --port 3001
配置反向代理:
// setupProxy.js
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
app.use(proxy([
"/posts",
], {
target: 'http://localhost:3001',
changeOrigin: true,
}));
};
然後,在另一個終端視窗中執行以下命令以啟動 React
應用程式:
$ yarn start
Chrome Local Overrides
從 Chrome 117
版本開始,引入了一項新功能,它允許開發者覆蓋XHR(XMLHttpRequest)
和fetch
請求的內容。您可以在不依賴真實後端資料的情況下進行開發和除錯。您可以自定義響應,模擬各種不同的情況,如成功響應、錯誤響應等,以確保您的網頁在各種場景下都能正常執行。
在 Chrome
瀏覽器的 Network
皮膚中,您可以右鍵點選特定的網路請求,並選擇"Override content"(覆蓋響應)。這將自動在開發者工具的 Sources
皮膚的 Overrides
部分生成一個本地的響應檔案。
可以在本地響應檔案自定義的響應內容,包括狀態碼、響應頭和響應體等,模擬不同業務場景的響應。
第三方Mock平臺
使用第三方的 Mock
平臺可以幫助你進行 API
的模擬和模擬資料的生成,下面以 Fast Mock
平臺為例:
在平臺配置了一個 Mock
介面 /user/address
然後配置代理:
// setupProxy.js
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
// 匹配反向代理
app.use(proxy([
"/user/address",
], {
target: 'https://www.fastmock.site/mock/6132bf44cf9f905257ca12ba9312ea10',
changeOrigin: true,
}));
// 或者使用中介軟體匹配路由,307 重定向
app.use((req, res, next) => {
// 匹配路由
if (/^\/user\/address$/.test(req.path)) {
res.redirect(307, 'https://www.fastmock.site/mock/6132bf44cf9f905257ca12ba9312ea10/user/address');
}
next();
});
};