我與我周旋久,寧作我
在前端開發中,使用 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();
});
};