前端Mock劫持Ajax实战指南:从XHR拦截到工程化方案
2026/9/14 4:46:48 网站建设 项目流程

作为一个常年跟后端联调掰扯、又经常被产品临时改需求的前端,我对Mock这件事的感情很复杂。一方面,没有Mock,我压根没法在后端接口没写好的时候按时交付页面;另一方面,要是Mock用得不够“聪明”,它带来的坑能让你在线上环境找bug找到怀疑人生。

之前我在团队里推过一轮“前后端并行开发”的流程,核心就是靠Mock把界面和交互先跑起来。结果不到两周,就有同事跑过来说“页面卡死了”“接口返回的数据不对”“明明代码里写死了数据,怎么请求还在发”。那段时间我几乎每天都在帮人擦屁股,也正是在这个过程里,把Mock劫持Ajax的坑踩了个遍。

这篇文章不聊那些理论层面的“Mock是什么”,就聊实战:从轻量拦截到框架级方案、从原生XHR劫持到路由级转发,每个方案我都会给出可直接抄作业的代码和参数选择逻辑。后面还附了一份我从翻车现场总结出来的排查手册,希望能让你少走几个弯路。

1. Mock劫持Ajax的核心思路与方案选型

先说一个很反直觉的点:很多前端新手理解的Mock,就是在代码里写死几组假数据,然后等后端好了再手动替换。这不算错,但远远不够。真正的Mock劫持,指的是在你的代码发起Ajax请求的那一刻,由Mock层把请求拦下来,返回你预设的数据,整个过程对业务代码完全透明。

这样做的好处很直接:你的页面代码从头到尾走的都是真实请求链路,后端的URL、参数格式、响应结构从一开始就被“钉死”了,后面切换真实接口时只需要把Mock关掉,不用改业务代码。项目越复杂,这种“以假乱真”的价值越明显。

1.1 为什么选择“劫持”而不是“改动代码”

我记得有一次,项目里有个数据报表页面,依赖四个接口的数据做聚合计算。当时后端只完成了两个接口,另外两个要等三天。如果靠“在页面里写死数据”,我至少得写两套渲染逻辑:一套是假的静态数据,一套是真的接口数据。等到后端就绪,我还得小心翼翼地删掉假逻辑,生怕漏改一处导致线上出问题。

但用劫持方案就不一样了。我可以在Mock配置里提前把四个接口的数据结构定义好,页面只需要按约定的结构写一套渲染逻辑。至于数据是Mock返回的还是后端返回的,页面根本不关心。这就像你在家试穿网购的衣服,试的是衣服本身合不合身,而不是先把自己饿瘦了再等衣服到货。

1.2 三种主流劫持方案的对比与场景匹配

目前前端领域常用的Mock劫持方案主要有三条路线,每条的优缺点和适用场景差别很大,我直接用表格做个对比:

方案劫持层面侵入性适用场景典型工具
正则/字符串替换URL请求入口简单接口代理、临时改个路径自写脚本、YAPI
Axios/Fetch拦截器请求库内部项目里统一用了axios或fetchaxios-mock-adapter
Service Worker浏览器网络层高(但最干净)复杂项目、需要离线开发Mock Service Worker(MSW)

从这张表能看出来,没有“最好的方案”,只有“当前阶段最合适的方案”。如果你的项目就是用axios封装了所有请求,那拦截器方案是最快的;如果你的项目要同时处理图片、脚本、接口各种资源,那Service Worker这种网络层面的方案更彻底。

我在团队里推行的方法是:能用拦截器就别上SW,除非跨域或离线需求真的很强。原因很简单,SW的调试难度和维护成本都更高,对刚接触Mock的同事不够友好。

1.3 一个容易被忽略的认知:Mock不是后端没做好才用的

这里我想多啰嗦一句。很多人觉得Mock是“后端拖后腿时的补救措施”,其实这是对Mock最大的误解。一个设计良好的Mock层,核心价值是帮你把前端代码与外部依赖解耦。哪怕后端接口已经全部就绪,你在做复杂交互调试、异常场景复现(比如超时、500、返回空数组)时,Mock依然比改后端代码方便得多。

我自己的习惯是,哪怕后端已经提供了可用接口,我也会在本地留一套Mock配置。不是为了造假,而是为了能随时复现“接口挂了”的极端情况,看看页面到底会怎么表现。这在你处理线上紧急工单时特别有用——你能在本地快速还原问题,而不是干等着运维反馈。

2. 从零开始:手写一个Mock劫持Ajax的最小实现

聊完选型逻辑,我们直接进入实操。要从根源上理解Mock劫持Ajax的原理,最好的办法不是立刻引入各种库,而是自己动手写一个最简版本。你会惊讶地发现,核心代码量比想象中少得多。

2.1 拦截XMLHttpRequest:看懂浏览器请求的最小闭环

在浏览器环境下,几乎所有Ajax请求最终还是通过XMLHttpRequest(简称XHR)或者fetch API发出的。jQuery的ajax、axios在浏览器端的适配器,底层都是XHR。所以,只要我们能在全局替换掉window.XMLHttpRequest,就能实现“上游拦截”。

先看这段最小实现:

// mock-xhr.js (function () { const RealXHR = window.XMLHttpRequest; function MockXHR() { const realXhr = new RealXHR(); this._realXhr = realXhr; this._mockUrl = null; this._mockData = null; // 把真实XHR实例上的关键属性和方法透传 Object.defineProperty(this, 'readyState', { get: () => realXhr.readyState, }); Object.defineProperty(this, 'status', { get: () => realXhr.status, }); Object.defineProperty(this, 'responseText', { get: () => realXhr.responseText, }); this.open = function (method, url, async) { this._method = method; this._url = url; // 注意:这里不能调用真实xhr的open,因为我们要决定是否劫持 }; this.send = function (body) { if (this._matchMock(this._url)) { // 命中Mock:直接模拟异步返回 setTimeout(() => { this._mockData = this._getMockData(this._url); this._realXhr.readyState = 4; this._realXhr.status = 200; this._realXhr.responseText = JSON.stringify(this._mockData); if (this.onreadystatechange) { this.onreadystatechange(); } if (this.onload) { this.onload(); } }, 100); } else { // 未命中:走真实请求 realXhr.open(this._method, this._url, true); realXhr.send(body); } }; this.setRequestHeader = function (header, value) { realXhr.setRequestHeader(header, value); }; } MockXHR.prototype._matchMock = function (url) { return window.__MOCK_CONFIG__ && window.__MOCK_CONFIG__.hasOwnProperty(url); }; MockXHR.prototype._getMockData = function (url) { return window.__MOCK_CONFIG__[url]; }; window.XMLHttpRequest = MockXHR; })();

这段代码的核心逻辑就三件事:

  1. send之前,先判断请求URL是否在Mock配置表里;
  2. 如果命中,就不发真实请求,用setTimeout模拟异步返回;
  3. 通过修改readyStatestatus,让回调正常触发。

这只是一个教学版的实现,真实项目里你还要处理onerrorwithCredentialstimeoutupload进度等场景。但它的存在价值是让你看清:所谓“劫持”,本质上是替换了浏览器的默认行为,而不是什么黑魔法。

2.2 轻量级进阶:用正则做灵活匹配,避免URL写死

上面的代码有个很明显的问题:Mock配置表的key是完整URL,如果后端接口路径里带了动态参数(比如/api/user/123),就没办法精确匹配了。实际开发中,这种动态路径太常见了,所以我一般会把匹配规则升级成正则匹配。

// 匹配规则升级:支持正则 MockXHR.prototype._matchMock = function (url) { const config = window.__MOCK_CONFIG__; if (!config) return false; return Object.keys(config).some((pattern) => { return new RegExp(pattern).test(url); }); }; MockXHR.prototype._getMockData = function (url) { const config = window.__MOCK_CONFIG__; const matchedKey = Object.keys(config).find((pattern) => { return new RegExp(pattern).test(url); }); return config[matchedKey]; };

配置表就变成这样:

window.__MOCK_CONFIG__ = { '/api/user/\\d+$': { id: 1, name: '张三', age: 28 }, '/api/order/list': { code: 0, data: [] }, };

这里有个细节值得注意:正则表达式在配置表里是用字符串写的,所以\d要写成\\d。我第一次用这个方案时就是被这个转义坑的,匹配了半天不生效,还以为是逻辑写错了。这也是“看起来简单,做起来踩坑”的典型。

2.3 代码的局限性:为什么企业级项目不推荐自己维护

手写实现有一个逃不开的问题:随着Mock规则越来越多,代码会变得越来越不可控。你不仅要处理各种请求方法(GET/POST/PUT/DELETE),还要处理查询参数、请求体、请求头,甚至要模拟延迟、超时、网络错误。这些逻辑堆在一起,维护成本会呈指数级上升。

所以,我现在的建议是:手写代码适合学习原理、适合一次性脚本,但如果是长期项目,直接用社区维护的成熟库更稳妥。用别人的库,不只是因为代码现成,还因为那些边边角角的兼容性问题已经被大量用户踩过坑了。

2.4 实战推荐:axios-mock-adapter的配置与参数说明

如果你的项目里统一用的是axios,那我最推荐的Mock方案是axios-mock-adapter。理由很简单:它直接拦截axios的请求适配器,不需要改window.XMLHttpRequest,也不影响非axios发起的请求,侵入性控制得刚刚好。

安装和基本用法如下:

npm install axios-mock-adapter --save-dev
import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; // 创建一个mock实例,并绑定到axios实例上 const mock = new MockAdapter(axios, { delayResponse: 300 }); // 模拟GET请求,带正则匹配动态参数 mock.onGet(/\/api\/user\/\d+/).reply((config) => { const userId = config.url.split('/').pop(); return [200, { code: 0, data: { id: userId, name: '用户' + userId, role: 'admin', }, }]; }); // 模拟POST请求,读取请求体中的参数 mock.onPost('/api/login').reply((config) => { const { username, password } = JSON.parse(config.data); if (username === 'admin' && password === '123456') { return [200, { code: 0, token: 'mock-token-123', expires: 7200 }]; } return [401, { code: 1001, message: '用户名或密码错误' }]; }); // 模拟网络超时 mock.onGet('/api/slow').timeout(); // 模拟服务器500错误 mock.onGet('/api/error').reply(500, { message: '服务器内部错误' });

这段代码里的每个onGetonPost都像一个路由规则,按顺序从上到下匹配,命中了就返回预设内容。注意看第三段代码:mock.onGet('/api/slow').timeout(),这一行能模拟接口超时。这在调试前端loading状态、超时重试逻辑时特别有用,省去了找后端帮忙制造故障的时间。

2.5 必踩的坑:包装过的axios实例需要单独绑定

这里有个高频翻车点,我必须拿出来单独讲。很多公司的前端项目会自己封装一个request.js,内部创建了一个独立的axios实例:

// request.js import axios from 'axios'; const service = axios.create({ baseURL: process.env.VUE_APP_BASE_URL, timeout: 15000, }); service.interceptors.request.use(/* ... */); service.interceptors.response.use(/* ... */); export default service;

如果你像我之前一样,在入口文件里写new MockAdapter(axios, ...),你会发现Mock根本不生效。原因很简单:业务代码里使用的是自定义的service实例,而不是全局的axios对象。MockAdapter绑定在全局axios上,拦截链路压根没有经过它。

正确做法是绑定到你的服务实例上:

import service from '@/utils/request'; import MockAdapter from 'axios-mock-adapter'; // 关键:绑定到service实例,而不是全局axios const mock = new MockAdapter(service, { delayResponse: 200 }); mock.onGet('/api/user/info').reply(200, { code: 0, name: '绑定了service实例', });

记住一个原则:MockAdapter绑定的axios实例,必须和业务代码里发请求的那个实例是同一个。这个坑我至少帮三个同事排查过,每次都是查半天才发现是实例绑错了。

3. 你一定会遇见的翻车现场:切换Tab页面卡死了

前面铺垫了这么多基础内容,现在进入真正的“翻车实录”。我选一个最具代表性的案例来完整复盘:在Tab页切换场景下,Mock劫持导致的页面卡顿与白屏问题。这个问题几乎每个用过Mock的人都会遇到,但很多人不知道根因在哪。

3.1 问题现象:是Mock的锅还是代码的锅

事情是这样的:项目里有个订单管理页面,顶部有“全部订单”“待付款”“已发货”“已完成”四个Tab。每次切换Tab,页面会重新请求对应的订单列表接口。上线前我在本地Mock环境检验功能,发现一个诡异的现象:第一次切换Tab一切正常,第二次切换到某个Tab时,页面就开始卡顿,连续快速切换五次左右,页面直接白屏

当时我第一反应是页面性能问题,可能是渲染太多DOM导致的。但看了Performance面板之后,发现CPU占比并不高,反而网络请求列表里出现了大量挂起的请求。顺着这个线索继续查,才发现根子出在Mock的匹配逻辑上。

3.2 根因分析:Mock规则混乱导致请求堆积

用axios-mock-adapter的时候,我写了一个很“粗犷”的拦截规则:

mock.onGet(/\/api\/order/).reply((config) => { // 根据URL里的status参数返回不同数据 const status = new URL(config.url).searchParams.get('status'); return [200, generateOrderList(status)]; });

这个正则匹配了所有包含/api/order的GET请求,理论上没问题。但我忽略了一点:axios-mock-adapter的规则匹配是有顺序的,一旦前面的规则没有精确匹配,它会继续尝试后面的规则,直到全部遍历完。而我在这个规则之前还定义了一个模糊匹配:

mock.onGet(/\/api\/.*/).reply(200, {});

于是,一个糟糕的连锁反应出现了:当请求URL是/api/order/list?status=1&page=2时,正则/\/api\/.*/先被尝试,它能匹配,所以请求被立即返回{}。而更具体的/api/order规则根本没有机会执行。页面拿到空数据,渲染异常、状态混乱,再叠加路由切换时旧请求未取消,就出现了“越切越卡”的现象。

3.3 定位问题的完整排查链路:从Performance到Network

我一般排查这种问题有个固定套路,这里分享下具体步骤:

  1. 打开Chrome DevTools的Performance面板,录一段“连续切换Tab”的操作,看主线程的Tasks分布;
  2. 如果发现大量XHR任务堆积,切到Network面板,勾选“Fetch/XHR”过滤,看请求状态;
  3. 如果发现一堆“pending”状态的请求,基本可以锁定是请求层的问题,与渲染无关;
  4. 在Network里点开某个pending请求,查看它所在的并发队列和延迟时间;
  5. 回到代码里逐个检查MockAdapter的规则匹配顺序,重点看是否有“宽泛规则抢在具体规则前面”的情况。

我这套链路走完,大概花了二十分钟,其中一半时间都在怀疑自己写的业务代码。所以也给你们提个醒:遇到页面卡死,先把请求层排查干净了,再去看渲染层

3.4 解决方案:窄化Mock规则,并合理组织匹配顺序

修复方案其实不复杂,核心就两点:规则要窄,顺序要正确

首先是窄化规则。把模糊匹配/\/api\/.*/改成具体的路径:

mock.onGet('/api/user/info').reply(200, mockUserInfo); mock.onGet(/\/api\/order\/list/).reply((config) => { // 具体的订单列表逻辑 }); mock.onGet(/\/api\/order\/detail\/\d+/).reply((config) => { // 具体的订单详情逻辑 });

其次是规则顺序。把所有onGetonPost按“精确到模糊”的顺序排列,像路由表一样管理。axios-mock-adapter虽然自带“先到先得”的匹配机制,但我们在书写时就该把顺序控制好,不能指望别人去猜。

最后是清理机制。如果项目里存在定时器或者轮询请求,记得在Mock切走时清理干净,避免请求在后台无限堆积。我习惯在Mock配置里额外导出一个reset方法,统一处理这些清理逻辑。

这次翻车让我学到一个很深的教训:Mock规则写得太宽,等于没有Mock。它表面上帮你“什么都拦一下”,实际上给了你一堆错误数据,掩盖了真正的问题。

4. 进阶:vue3 + TS项目里的Mock实践方案

随着团队项目逐步迁移到Vue3 + TypeScript,我又花了些时间把Mock方案做了升级。这里直接说结论:在Vue3 + TS的工程里,我最终采用的是“API层抽象 + Vite代理 + 独立的Mock服务”三段式结构。这套组合既保留了Mock的灵活性,又解决了TypeScript类型约束下的代码提示问题。

4.1 为什么单独提Vue3 + TS:类型与模块化带来的新问题

Vue3项目里普遍使用了Composition API和模块化组织代码,这给Mock带来的挑战是:每个接口的入参出参都需要有类型定义,Mock数据如果与类型不一致,会在运行时暴露奇怪的问题。比如TypeScript编译期没报错,但Mock返回的数据少了一个字段,页面渲染时直接undefined,而你很难第一时间想到是Mock数据的问题。

另一个痛点是:Vue3项目通常配合Vite使用,Vite的开发服务器本身就提供了代理能力。如果我们把Mock直接挂在window.XMLHttpRequest上,不仅绕过了Vite的代理配置,还会在多个模块之间产生全局污染。

4.2 三段式结构:接口抽象层、Mock数据层、条件启用

这三段式结构的组织方式如下:

第一段是接口抽象层(api/目录),所有请求都通过这里发出去,返回类型用TS定义好:

// api/user.ts import request from '@/utils/request'; export interface IUserInfo { id: number; name: string; avatar: string; roles: string[]; } export function getUserInfo() { return request.get<IUserInfo>('/api/user/info'); }

第二段是Mock数据层(mock/目录),每个模块维护自己的Mock数据和规则:

// mock/user.ts import { MockAdapter } from 'axios-mock-adapter'; import type { IUserInfo } from '@/api/user'; export function mockUser(adapter: MockAdapter) { adapter.onGet('/api/user/info').reply(200, { id: 9527, name: 'Mock老张', avatar: 'https://example.com/avatar.png', roles: ['admin', 'editor'], } satisfies IUserInfo); }

使用satisfies语法是TS 4.9+的特性,它能确保Mock数据在结构上符合IUserInfo,但不会像as那样强制断言。这一步能提前拦住“Mock数据少字段”的低级错误。

第三段是条件启用(通常写在main.ts或独立的mock/index.ts):

// mock/index.ts import MockAdapter from 'axios-mock-adapter'; import service from '@/utils/request'; import { mockUser } from './user'; import { mockOrder } from './order'; const enabled = import.meta.env.VITE_ENABLE_MOCK === 'true'; export function setupMock() { if (!enabled) return; const adapter = new MockAdapter(service, { delayResponse: 200 }); // 顺序:先具体,后模糊 mockUser(adapter); mockOrder(adapter); }

main.ts里调用:

import { setupMock } from './mock'; setupMock();

通过环境变量VITE_ENABLE_MOCK控制Mock的开启与关闭。开发环境设为'true',生产构建自动设为'false',从源头杜绝“把Mock带上了线上”的惨剧。

4.3 Vite代理与Mock的联动:跨域与路径前缀问题

在Vite项目里,如果你配置了server.proxy,那Mock劫持的URL就必须特别注意。假设你的request.js里配置的baseURL/api,Vite代理把/api转发到http://backend.example.com,那么MockAdapter在匹配时也要匹配/api开头的路径。

很多人在这一步被坑,是因为他们用了后端的完整地址(比如http://backend.example.com/api/user/info)作为Mock匹配规则。问题是,在开发环境下这个完整地址是会被Vite代理转发掉的,请求根本没发到MockAdapter那里。所以你一定要理清楚:在Vite代理生效的情况下,前端代码里看到的URL是以/api开头的相对路径,Mock规则只需要匹配这个相对路径就对了。

4.4 环境开关与生产安全:让Mock离线上远远的

新手最慌的一个问题是“Mock数据会不会不小心跑到线上”。我可以给你一个比较绝对的建议:不要只靠环境变量来控制Mock,还要在构建层面做“死”

我的做法分三层:

  1. import.meta.env.VITE_ENABLE_MOCK只有在development模式下才可能为true
  2. vite.config.ts里只在serve命令时加载Mock插件,执行vite build时彻底不打包Mock代码;
  3. mock/index.ts里加一个运行时校验,检查当前环境process.env.NODE_ENV === 'production'时直接抛错。

三个保险一起上,基本杜绝了Mock“偷渡”到线上的可能性。

5. 从劫持到模拟:Service Worker方案能不能解决所有问题

聊完了最常用的拦截器方案,很多人会问:那是不是用Service Worker做Mock就一劳永逸了?MSW(Mock Service Worker)确实是目前前端Mock领域最“正统”的方案,因为它工作在浏览器网络层,连图片、脚本、CSS这些资源都能拦截,不局限于XHR/Fetch请求。但它也有自己的麻烦事。

5.1 Service Worker的优势与劣势:不要神化它

MSW最大的优势是“真实”。它在浏览器和网络之间架起一层拦截,业务代码里发请求跟真实的浏览器环境几乎一致,不需要关心你用的是axios还是fetch,也不需要关心你封装了几个实例。对于一些老项目或复杂项目,这个特性特别诱人。

但它的代价是:初始化复杂、调试困难、在部分浏览器环境有兼容性问题。你需要在public目录下放mockServiceWorker.js,需要在入口处动态注册,还需要处理serviceWorker的缓存更新策略。如果你只是想快速给一个页面加几个Mock接口,用MSW反而显得笨重。

5.2 什么场景下值得切换到MSW

我目前只在两种场景下会主动切换到MSW:

一是项目里不止用了axios,还存在大量fetch、图片请求等需要模拟的场景。二是需要做离线开发或沉浸式开发,希望整个页面“断网”也能完整运行。除此之外,用axios-mock-adapter这种库就足够了。

5.3 一个折中方案:用猴子补丁实现轻量MSW效果

如果你既想要SW的真实网络层拦截,又不想被它的复杂度劝退,可以试试一个取巧的思路:在开发环境用vite-plugin-pwa的思路,注册一个临时Service Worker,只拦截API前缀的请求。但这个方案对前端基建能力要求偏高,说实话不太适合绝大多数团队。

我个人还是更倾向于把简单的事做简单。Mock的核心是解决数据依赖问题,不是展示技术能力。如果你一上来就上重型方案,团队同事的学习成本会很高,反而容易放弃使用。

6. 常见问题速查表与避坑锦囊

文章最后,我把这几年用Mock劫持Ajax遇到的高频问题整理成一张速查表。这不是网上随处可见的“知识点大全”,而是我自己和团队同事真实踩过、并有对应解决方法的实战索引。

6.1 必知必会的排查问题清单
问题现象可能原因解决办法
Mock不生效,请求走了真实接口MockAdapter绑定了错误的axios实例确保绑定的是业务代码中实际使用的实例
页面白屏或数据不显示Mock数据缺少字段、字段类型不符合TS定义satisfies做静态检查,运行时打日志校验
切换Tab后页面越来越卡Mock规则写得太宽,导致请求异常堆积窄化匹配规则,精确到具体路径
接口返回200但页面没反应Mock返回结构与封装好的响应体结构不一致统一在code/data/message结构下做Mock数据
生产环境出现了Mock数据环境变量控制失效,Mock代码被打了进去构建层面彻底排除,运行时校验NODE_ENV
正则配置不生效字符串里的反斜杠未转义\\d而不是\d
分页参数被忽略用了固定匹配而非正则/函数匹配改用onGet(/regex/)或读取config.params
POST请求参数读不到config.data可能是字符串而非对象使用JSON.parse(config.data),注意判空
6.2 高价值实战技巧:分页、鉴权、延迟这批细节怎么处理

Mock分页是我见过最容易“随便写写”的环节。很多人在Mock里直接写死一页数据,导致前端分页器不管怎么切页码都在重复渲染相同内容。正确的做法是让Mock支持从请求参数里读取pagepageSize

mock.onGet('/api/order/list').reply((config) => { const { page = 1, pageSize = 10, status = '' } = config.params || {}; const filteredList = allOrders.filter((item) => item.status.includes(status)); const start = (page - 1) * pageSize; const end = start + pageSize; return [200, { code: 0, data: { list: filteredList.slice(start, end), total: filteredList.length, page: Number(page), pageSize: Number(pageSize), }, }]; });

注意config.params这里,如果你在业务代码里是通过request.get('/api/order/list', { params: { page } })这种方式传参的,那axios-mock-adapter会自动解析到config.params上。如果你用的是/api/order/list?page=1这种拼接在URL里的方式,就得手动用new URL(config.url).searchParams.get('page')去取。两种方式都能用,但要保持前后一致。

延迟也是一个值得细调的参数。我习惯在Mock初始化时设置一个不大不小的延迟(200~300ms),这样页面的loading态能被自然触发,联调时也不会因为“响应太快”而漏掉loading体验。注意,延迟不是越大越好,超出一秒会严重影响开发效率。

6.3 从工程化视角对Mock的几点建议

最后给几条工程化层面的建议,是我在推行Mock流程后感受到的切身体会:

  1. Mock配置要与接口文档同步维护。接口变更时,在改代码前先把Mock数据更新了,这样能逼迫前后端对接口结构达成一致,减少后期扯皮。

  2. Mock目录要按业务模块拆分,不要把所有规则堆在一个文件里。我的习惯是mock/下对应api/的目录结构,一个模块一个文件,互不干扰。

  3. 团队里要有一个人专门负责Mock基建。不是让大家各自为战,而是统一封装、统一升级。等有人踩了坑、填了坑,其他人只要跟着升级就能少走弯路。

  4. 留一条“异常出口”。Mock环境偶尔也需要模拟真实环境,比如暂时关掉Mock跑一两个真实接口,验证联调是否通畅。我一般在Mock配置里加一个名单机制,名单内的URL放行、走真实请求,名单外的全部Mock。

写在最后

这大半年的Mock劫持Ajax实战,让我最大的改变是:不再把Mock当成“临时凑合”的方案,而是作为前端工程化里一个正式的组成部分。它不只是帮你在后端写好接口前“撑场子”,更是帮你提前暴露各种边界情况、接口结构问题,以及你自己的代码在数据缺席时会怎么表现的照妖镜。

回想那次把axios实例绑错导致的“Mock失效”,其实问题本身很小,但它让我意识到了解原理的重要性。如果你不了解底层是怎么把请求“偷梁换柱”的,出问题时你根本不知道往哪里排查。所以这篇文章里我特意保留了那段手写XHR劫持的最小实现,就是希望你在用各种现成库的时候,心里清楚它们的工作机制。

如果这篇文章能让你少踩一个Mock的坑,那它的价值就超额完成了。以后如果还有关于Mock和前端工程化的新坑,我会继续回来补充,和你们一起把这锅夹生饭慢慢炖熟。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询