ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

前端 Mock 数据方案全解析:MSW、抓包与契约驱动

前端 Mock 数据方案全解析:MSW、抓包与契约驱动 做过几年前端的人大概都经历过这种场面需求评审刚过设计稿还在改接口文档只写了个标题产品经理已经在群里问“明天能不能先看个演示”。这时候你要么干等要么自己把数据造出来。前端 mock 数据这件事说到底就是在这段空白期里让页面先跑起来——它不是什么偷懒的小技巧而是把前端从“等接口”的被动状态里拽出来的常规工程手段。从最土的本地 JSON 文件到请求层的拦截再到抓包工具改写响应、后端契约驱动的 Mock 服务器每条路我都趟过也都在不同的项目阶段吃过各自的亏。这篇就把这些方式挨个拆开讲清楚它们各自的原理是什么、什么阶段该用哪个、参数怎么定、坑在哪里、出了问题按什么顺序排查。刚入行的同学可以照着抄作业做过两三年的也能在里面找到一些能直接塞进现有项目的开关设计和目录约定。读完之后你至少应该能回答一个问题手上这个项目到底该选哪种 mock 方式为什么。1. 前端 Mock 到底在解决什么问题先把这个概念摆正。很多人一提 mock 就想到“造假数据”其实它解决的是三个完全不同的诉求混在一起谈就很容易选错方案。第一种是并行开发后端还没写完前端要先把页面结构和交互逻辑跑通此时的 mock 数据只需要形状对、字段够用就行。第二种是边界场景构造接口本身已经有了但你想看空列表、超长昵称、金额为 0、时间跨年这些情况真实环境里很难手动造出来。第三种是稳定复现某个 bug 只在特定的响应组合下出现你需要让接口每次都返回一模一样的数据方便断点调试。这三种诉求决定了你该用哪一层的手段。并行开发适合用请求拦截或者 Mock 服务器因为改起来快、大家共享一份边界场景构造适合在拦截层或者浏览器 DevTools 的响应覆盖里做因为不需要动代码就能临时改一个字段稳定复现则更倾向于本地代理或者抓包工具的固定响应因为它能锁死整个响应体。1.1 先想清楚数据是给谁看的有个判断标准特别实用这份 mock 数据是要提交到代码仓库、给整个团队共用的还是只给你自己临时看几眼的前者必须走工程化的路子放在约定的目录下有类型声明有开关控制随时能关掉后者怎么方便怎么来DevTools 里改一下响应体、本地起个静态文件十分钟搞定没必要为了“规范”给自己加负担。我见过不少团队在这件事上走极端。有的把所有 mock 数据都硬编码在组件里上线前清洗不干净法务一个字段一个字段地查有的又过度设计专门搭了一套 mock 平台结果没人维护半年后文档和实际返回对不上新人照着文档调半天最后发现是 mock 数据过期了。合理的做法是按阶段分配项目初期广撒网中期收敛到一层统一的拦截交付前把开关彻底拆掉或者指向真实接口。1.2 数据不一致的根源在哪里“数据不一致”这个词在搜索里出现频率很高但它其实是好几类问题的统称。第一类是字段名和类型对不上后端返回user_id下划线前端按userId驼峰取取出来是undefined页面显示空白但控制台又不报错最难查。第二类是结构和空值语义不同后端在没有数据时返回null前端按数组处理做了.map()直接白屏。第三类是mock 数据和真实数据本身就有偏差mock 里手机号是 11 位数字字符串真实数据里带了86前缀正则校验就挂了。这三类里前两类靠类型声明和约定能解决一大半第三类必须靠“从真实响应里拷贝一份脱敏样本”来兜底。我的习惯是任何 mock 数据在落地前先想办法搞到一条真实响应字段名、嵌套层级、空值形态全部对齐然后再把值替换掉。这一步花十分钟能省掉后面一整天的对不上。2. 硬编码与本地 JSON最土但最快的起手式新手最先接触的肯定是这一种新建一个data.json里面塞几个对象组件里import进来直接用。它谈不上优雅但在项目刚开始、页面结构还没定下来的时候效率是最高的。因为它零配置、零依赖、不需要起任何额外服务鼠标点一下就能看到效果。合理的组织方式是按业务模块分目录而不是所有数据堆一个文件。比如mock/user.json、mock/order.json、mock/common/dict.json字典类数据单独抽出来因为它在多个页面复用。文件内部建议保留真实接口的完整结构包括外层那层code、message、data别只留data里的内容否则后面切换到真接口时取值路径要全部改一遍。2.1 JS 模块导出假数据更适合造边界值JSON 只能写死值而.ts模块可以写逻辑。当前端需要造一批有规律的边界数据时模块导出会比 JSON 好用得多。比如要测一个分页列表需要 500 条数据还要覆盖长文本和特殊字符// mock/generator/user.ts const NICKNAMES [张三, 李四, 王五, 这是一条特别长的昵称用来测试表格换行是否正常]; // 生成过去 N 天内的随机时间戳毫秒 function randomTimestampWithin(days: number): number { const now Date.now(); const range days * 24 * 60 * 60 * 1000; // 天数换算成毫秒 return now - Math.floor(Math.random() * range); } // 用固定种子生成可复现的伪随机数保证每次刷新数据一致 function createRandom(seed 20260101) { let s seed; return () { s (s * 9301 49297) % 233280; return s / 233280; }; } export function buildUserList(total 500) { const rand createRandom(); return Array.from({ length: total }, (_, i) ({ id: 10000 i, userId: U${10000 i}, nickname: NICKNAMES[Math.floor(rand() * NICKNAMES.length)], amount: Number((rand() * 10000).toFixed(2)), status: rand() 0.7 ? 0 : 1, createdAt: randomTimestampWithin(30), })); }这里有两个细节值得说一下。randomTimestampWithin里的days * 24 * 60 * 60 * 1000是标准的天转毫秒别写成days * 86400000之外的花样虽然结果一样但前者更容易一眼看懂也方便改成年、小时。而那个createRandom是线性同余伪随机目的是让每次刷新页面时数据保持一致——真随机会导致你改了一个字段、刷新一下整张表全变了根本没法对比调试。这个坑我踩过不止一次后来所有需要随机值的 mock 都换成固定种子。2.2 硬编码方案的三个硬伤第一个硬伤是跟真实请求流程脱节。组件里直接import数据意味着拿不到 loading 状态、错误状态、超时这些真实场景。等切换到真接口那天会发现加载动画根本没写、失败重试也没做只能临时补。第二个硬伤是误上生产。静态 import 会被构建工具打进包里如果没人清理线上会存在一堆用不到的假数据。我见过最离谱的一次一个字典文件里带着测试用的内部人员名单被打进了生产 bundle虽然没暴露到页面上但通过 Source Map 就能翻出来。第三个硬伤是无法覆盖分页、搜索、排序这类交互。用户点了第二页数据还是第一页那几百条测试根本没法往下走。所以硬编码的定位要清楚它只适合“静态展示型页面”一旦涉及请求交互就该换方案了。注意硬编码的数据文件不要放在src下跟业务代码混在一起统一放到src/mock/data/这种带明显标识的目录方便上线前用脚本扫描清理也方便做代码审查时一眼识别。3. 请求拦截层MSW 为什么成了主流选择请求拦截的核心思路是不动业务代码在底层把真实的网络请求截住返回你预先准备好的数据。这样组件里写的还是axios.get(/api/user/list)切到真接口时一行代码都不用改。这是它比硬编码强的地方也是我认为在“并行开发”阶段最值得投入的一层。拦截的技术实现经历过几代演进。最早是重写XMLHttpRequest的open和send方法或者包装fetch思路简单但对axios的适配器、umi-request这类二次封装的库不太友好经常出现“拦截了但没生效”。后来出现 Service Worker 方案也就是 MSWMock Service Worker走的路子它在浏览器和页面之间加了一层能真正拦截网络请求连 DevTools 的 Network 面板里都能看到请求记录。3.1 手写 XHR 拦截理解原理用得上虽然不推荐在生产项目里手写但理解它的原理有助于排查“为什么 mock 没生效”。下面这段是最小实现// 仅用于理解原理不建议在正式项目中使用 (function interceptXHR() { const OriginalXHR window.XMLHttpRequest; const mockRules [ { test: (url) /\/api\/user\/list/.test(url), response: { code: 0, message: ok, data: { list: [], total: 0 } }, }, ]; function MockXHR() { const xhr new OriginalXHR(); const originalOpen xhr.open; const originalSend xhr.send; xhr.open function (method, url, ...rest) { this.__url url; return originalOpen.call(this, method, url, ...rest); }; xhr.send function (body) { const rule mockRules.find((r) r.test(this.__url)); if (!rule) return originalSend.call(this, body); // 手动造出一次成功的响应 setTimeout(() { Object.defineProperty(this, readyState, { value: 4 }); Object.defineProperty(this, status, { value: 200 }); Object.defineProperty(this, responseText, { value: JSON.stringify(rule.response) }); this.dispatchEvent(new Event(readystatechange)); this.dispatchEvent(new Event(load)); }, 100); }; return xhr; } window.XMLHttpRequest MockXHR; })();这段代码能说明三个关键点拦截必须发生在业务代码请求之前所以脚本要放在最前面或者在应用初始化时执行readyState、status、responseText这几个属性要能被读到事件要手动触发否则 Promise 永远不会 resolve。知道了这些当你遇到“请求发出去了但 Promise 一直挂起”的情况就能立刻定位到是事件没派发。3.2 MSW 在 Vue3 TS 项目里的落地步骤MSW 的优势在于它用 Service Worker 在浏览器层拦截业务代码完全无感知并且能同时跑在 Node 环境单元测试里一套 handler 两处复用。下面是完整的落地流程。第一步装依赖把 Service Worker 脚本生成到公共目录npm i -D msw npx msw init public --save执行完会在public/下生成mockServiceWorker.js这个文件必须能被浏览器以根路径访问到所以放在public或static目录不能放进src里被打包。第二步写 handler。这里以msw2.x 的写法为例注意 API 跟 1.x 差别很大网上很多老教程是rest.get新版已经换成http.get// src/mocks/handlers.ts import { http, HttpResponse, delay } from msw; import { buildUserList } from ./generator/user; export const handlers [ http.get(/api/user/list, async ({ request }) { const url new URL(request.url); const page Number(url.searchParams.get(page) ?? 1); const size Number(url.searchParams.get(size) ?? 20); // 模拟真实网络延迟方便观察 loading 状态 await delay(300); const all buildUserList(500); const start (page - 1) * size; // 分页起始下标 return HttpResponse.json({ code: 0, message: ok, data: { list: all.slice(start, start size), total: all.length, page, size, }, }); }), // 模拟一个失败场景用于测试错误处理分支 http.post(/api/order/create, async () { await delay(200); return HttpResponse.json( { code: 40001, message: 库存不足 }, { status: 200 }, ); }), ];分页那段const start (page - 1) * size是最容易被写错的地方第 1 页要从下标 0 开始写成page * size会导致第一页永远是空的然后你会以为是 mock 没生效白白排查半天。第三步在入口处按环境变量决定是否启动// src/main.ts import { createApp } from vue; import App from ./App.vue; async function bootstrap() { // 只有显式开启 mock 开关时才启动避免污染测试或预发环境 if (import.meta.env.DEV import.meta.env.VITE_USE_MOCK true) { const { worker } await import(./mocks/browser); await worker.start({ onUnhandledRequest: bypass, // 没匹配到的请求直接放行别报错 }); } createApp(App).mount(#app); } bootstrap();// src/mocks/browser.ts import { setupWorker } from msw/browser; import { handlers } from ./handlers; export const worker setupWorker(...handlers);onUnhandledRequest这个配置很关键。默认值是warn意思是没匹配上的请求会在控制台刷警告。项目里静态资源、埋点、CDN 请求一大堆警告多到你根本看不见真正的信息所以开发时设成bypass更清爽。但要注意如果你明明配了 handler 却还是走了真实请求先把这里临时改成error它会在请求未匹配时直接抛出异常能逼你立刻发现规则写错了。第四步在.env.development里加开关VITE_USE_MOCKtrue这样做的意义在于mock 的开启变成了一件显式的、可追踪的事。CI 构建默认不带这个变量就不会被打进去某个同学本地想看真实数据改一下环境变量重启即可。3.3 顺带说下 vite-plugin-mock 这类方案有些项目用的不是 MSW而是vite-plugin-mock它在 Vite 的 dev server 中间件层面拦截配置写在vite.config.ts里。它的好处是天然在 Node 层能直接读本地文件、能写日志拦截规则集中。缺点是只作用于开发服务器跑单元测试时用不了而且配置和 Vite 强绑定换构建工具就得重写。我一般的取舍是团队需要单元测试里也复用同一套 mock选 MSW只是单纯想让 dev server 返回假数据选插件更省事。两者不冲突也有项目同时用。3.4 请求拦截方案的横向对比方案侵入性生效范围能否复用真实请求链路上手成本适用阶段硬编码 / 本地 JSON高改业务代码仅页面数据否极低页面原型期手写 XHR / fetch 拦截无浏览器内部分中临时调试MSW无浏览器 Node是中全程vite-plugin-mock无开发服务器否低开发期抓包工具改响应无整个系统是低联调、复现Mock 服务器无所有客户端是高多端协作这张表建议收藏下次团队讨论“我们用什么 mock”的时候直接对着阶段和诉求圈一行比争论半小时有效得多。4. 抓包工具与代理层不动代码的那种改法抓包工具各种 HTTP 调试代理的定位跟前面完全不一样。它工作在系统的网络层任何走这台机器的请求它都能看到所以它不仅能改浏览器里的请求还能改小程序开发者工具、桌面客户端的请求。当你要验证“这个接口换一批数据页面表现是不是正常”时它是最快的因为不用改一行代码、不用重启服务。常见的用法是配置一条匹配规则匹配到指定的 URL 后返回本地文件或者手写的响应体。响应内容可以是固定的 JSON 文件也可以用变量、时间戳、随机数做动态替换。整套流程的核心是“匹配规则 响应内容”两件事看起来简单但真正卡住人的往往是规则本身。4.1 为什么改了响应却不生效“改了响应数据但页面上没变化”是这类工具最高频的求助。按下面的顺序排查通常三步之内能定位。第一匹配规则没命中。很多工具的匹配表达式默认是“包含”还是“正则”语义不一样。URL 里带查询参数时如果你只写了路径部分可能匹配不上反过来如果你写了完整的带参 URL但实际请求的参数顺序变了也会匹配失败。最稳的做法是用正则匹配纯路径/api/user/list这种参数交给后面的处理逻辑。第二缓存和协商缓存拦截了请求。浏览器对GET请求如果命中了Cache-Control或者ETag的协商缓存它压根不会发出真实的网络请求抓包工具自然看不到你改的响应也就无从生效。排查方式是打开 DevTools 的 Network 面板看这条请求的 Size 列是不是显示(memory cache)或(disk cache)是的话勾上 Disable cache或者在后端响应头里临时去掉缓存相关字段。第三HTTPS 证书没信任。HTTPS 请求要被抓包工具解密必须让系统信任它的根证书。如果证书没装好浏览器会直接报证书错误请求根本走不到工具里如果只信任了一部分比如浏览器信任了但系统没有就会出现“有的客户端能抓到、有的抓不到”的诡异现象。这类问题在 macOS 和 Windows 上的表现不一样需要在系统证书管理里确认一遍。还有一类情况是客户端自带了独立的网络实现比如某些 App 的主进程不走系统代理或者小程序在真机模式下有自己的证书校验逻辑这些都会让你的规则失效。判断方法很简单看抓包工具里这条请求到底有没有出现。没出现就是根本没走到代理出现了但响应没变才是规则或缓存的问题。注意抓包工具的能力很强能修改别人的请求和响应所以一定要在自己的开发设备、自己负责的测试环境里用。任何针对非自有系统的响应篡改尤其是涉及身份校验、金额、权限判断这类字段的修改都属于明确的越界行为不要碰。真要做权限相关的界面调试正确做法是在自己的开发环境里 mock 一份带权限标识的响应或者让后端配合提供测试账号。4.2 本地代理转发让请求指向另一台机器比抓包更“正规”的一种做法是在本地起一个反向代理把/api前缀的请求转发到另一个地址。前端项目里 Vite 和 Webpack 都内置了这个能力// vite.config.ts import { defineConfig } from vite; export default defineConfig({ server: { proxy: { /api: { target: http://127.0.0.1:3000, // 指向本地 mock 服务 changeOrigin: true, // 只在需要重写路径时使用否则容易把真实路径改坏 // rewrite: (path) path.replace(/^\/api/, ), }, }, }, });changeOrigin: true的作用是让代理请求的Host头变成目标地址避免对方服务器因为 Host 不匹配而拒绝。而rewrite那一行我特意注释掉了因为见过太多人复制粘贴后忘了改结果请求路径被无声无息地改掉接口 404 却查不出原因。代理转发最大的价值在于跨设备调试。手机连同一个局域网把接口地址指向你电脑的 IP就能在真机上跑本地 mock 数据这在做移动端适配时非常有用。相比之下浏览器插件式的拦截只能在浏览器里生效真机完全用不了。5. 后端参与型 Mock契约先行与 Mock 服务器前面的方案都是前端自己扛做到一定程度就会遇到瓶颈多个端Web、小程序、App要共用一份数据或者测试同学要拿固定的数据跑自动化用例。这时候就该让后端参与进来核心思路是“契约先行”——先把接口的字段、类型、示例值定下来任何人可以基于这份契约生成 mock 数据。具体落地通常有两种形态。一种是把接口描述文件如 OpenAPI/Swagger 规范维护起来用工具直接根据描述生成一个可访问的 Mock 服务返回符合结构的假数据。另一种是团队自建一套 Mock 平台界面上配置路径、方法、响应模板发布后得到一个固定域名。两种形态的共同点是mock 数据的唯一来源是契约不再是某个人的本地文件。5.1 契约驱动的好处和代价好处非常直接前后端在写代码之前就先对齐了字段名和类型等真正联调的时候最大的那类“字段名对不上”的问题被提前消灭了。而且生成的 Mock 服务返回的数据结构天然符合约定前端写的取值逻辑换到真接口上基本不用改。代价是维护成本。契约文件不更新mock 服务就会一直返回过期结构反而比没有更糟——因为大家会默认它是准的。所以必须有人负责通常是后端主 R 或者架构同学在每个接口评审后同步更新。我见过做得好的团队把契约更新纳入了 CI接口代码和契约不一致时流水线直接失败虽然前期麻烦但长期看省下的联调时间非常可观。5.2 数据不一致的几种典型形态配合契约使用时下面这几种不一致最容易出现值得单独列出来对照检查。不一致类型典型表现排查方式预防手段命名风格前端取userId得到 undefined打印完整响应体核对字段名契约里明确命名规范空值语义无数据时返回 null前端按数组处理报错构造空结果集看是否白屏约定空集合统一返回[]数值类型金额 mock 是 number真实是 string用typeof打印类型契约里标注类型和精度时间格式mock 是时间戳真实是 ISO 字符串对比格式化函数的输入统一用时间戳或统一字符串枚举取值mock 里 status 是 0/1真实是字符串穷举枚举值契约里附枚举字典层级嵌套mock 少了一层data逐层打印取值路径契约里写完整示例这张表的用法很简单联调出问题时从上往下逐行排除通常第二三行就能命中。我习惯在项目的docs/里放一份新人入职时先看这个比自己摸索快很多。5.3 微前端场景下的一个额外注意点项目里用了微前端方案时主应用和子应用各自可能有自己的 mock 配置。子应用用 MSW 起了 Service Worker主应用也在拦截请求两边规则一重叠就会互相覆盖表现为“子应用接口返回了主应用的数据”。这种情况下把拦截全部收敛到主应用一层子应用只负责提供 handler 配置由主应用统一注册或者约定好各自只拦截自己前缀的路径比如子应用只处理/api/order/*。这个坑不常见但一旦踩上很难定位因为它表现出的现象跟业务逻辑毫无关系。6. 工程化落地把 Mock 开关当成一个功能来做方案选完之后真正决定这套东西好不好用的是工程化细节。前面反复提到“开关”这里展开说一下。核心原则只有一条mock 必须是一个可以明确开启、明确关闭、默认关闭的状态而不是散落在代码里的隐式行为。6.1 环境变量与目录约定推荐的目录结构大概是这样的src/ mocks/ handlers.ts // 所有请求规则按业务分文件后在这里汇总 browser.ts // 浏览器端启动逻辑 server.ts // Node 端启动逻辑供单元测试使用 generator/ // 数据生成器带固定种子的伪随机 user.ts order.ts data/ // 静态 JSON字典类数据放这里 dict.json环境变量只留一个VITE_USE_MOCKtrue时启动其余情况一律不启动。这样在预发和生产环境无论谁误提交了什么只要构建脚本没带这个变量mock 就永远不会生效。比起在代码里写if (process.env.NODE_ENV development)这种判断环境变量的可控性更强因为它是构建时注入的浏览器端改不了。单元测试里复用同一套 handler 也很简单// tests/setup.ts import { setupServer } from msw/node; import { handlers } from ../src/mocks/handlers; export const server setupServer(...handlers); beforeAll(() server.listen()); afterEach(() server.resetHandlers()); afterAll(() server.close());resetHandlers这一句不能省。有的用例为了测异常分支临时覆盖了某条规则如果不重置后面的用例会莫名其妙地继承上一个用例的行为出现“单独跑通过、一起跑失败”的经典问题。6.2 数据防丢失mock 数据的版本管理mock 数据要不要进版本库这个问题我犹豫过很久最后得到的结论是生成器和字典进大规模静态数据文件不进。生成器是代码逻辑变了需要 review字典是业务知识比如城市列表、状态枚举属于团队资产。而那种几百行的列表数据文件本质上是把数据库导出到了仓库里改动频繁、体积大、还容易夹带不该出现的测试数据直接加进.gitignore需要的人自己生成。如果确实需要一份固定的、可复现的数据集用前面提到的固定种子生成器来产出把种子值写进代码注释里谁都能重新算出同一批数据。这比提交一个巨大的 JSON 文件干净得多也避免了误提交真实数据带来的风险。注意任何 mock 数据里都不要出现真实的手机号、身份证号、银行卡号、内部人员姓名。造数据时统一用明显假的格式比如昵称用“测试用户A”手机号用13800000000这类保留号段。这件事在数据合规上没有任何商量余地。7. 常见问题速查与踩坑实录把踩过的坑整理成表出问题的时候按行对号入座比翻文档快。7.1 问题速查表现象最可能的原因快速验证方式处理办法handler 配了但请求还是打到后端Service Worker 没注册成功或路径前缀不匹配Network 面板看请求来源控制台看 SW 状态检查msw init生成的文件是否可访问匹配规则加通配页面一直 loading 不结束拦截后没触发响应事件看 Promise 是否一直 pending手写拦截时手动派发load事件改了响应没生效被浏览器缓存命中Network 面板看 Size 列是否为 cache勾选 Disable cache临时去掉缓存响应头抓包工具看不到请求未安装信任证书或客户端不走系统代理工具里完全没有这条记录安装并信任根证书确认客户端代理设置第一页数据为空分页下标算错打印start和size用(page - 1) * size每次刷新数据都变用了真随机连续刷新对比改用固定种子的伪随机单元测试单独过、一起跑失败handler 被上一个用例污染调整用例顺序复现每个用例后resetHandlers主应用和子应用数据串了两层拦截互相覆盖看返回的数据属于哪个模块收敛到单层拦截或按路径前缀隔离线上包里出现假数据静态 import 被打包搜索构建产物里的 mock 关键字mock 数据改为动态 import加构建期校验7.2 几个我反复用到的实操心得心得一先造假数据再写真组件。很多人的顺序是先写页面再补数据结果是页面写完了发现数据形状跟接口对不上返工。我的习惯是先把一条真实响应拷贝出来脱敏后存成样本然后照着样本写组件。这样即使后面接口改了字段改的也只是取值路径DOM 结构不用动。心得二给每个 mock 规则加注释说明来源。写清楚“这条规则对齐的是 xx 接口 v2 版本”别人接手时知道该找谁确认。半年后你自己回来看也会感谢当时的自己。心得三延迟不要设成 0。网络延迟设为 0 的后果是 loading 状态一闪而过你根本看不出加载动画有没有问题、骨架屏有没有生效。设个 200~500 毫秒既能看清过渡又不至于烦人。需要专门测弱网时单独加一条延迟 3000 毫秒的规则用完删掉。心得四主动造失败场景。真实项目里最容易被忽略的就是错误分支。我通常在 handlers 里给每个接口都配一条可通过开关触发的失败规则切换成本极低却能提前发现一堆“接口挂了白屏”的问题。业务代码里那句catch有没有写好只有真出错的时候才知道。心得五交付前跑一遍真实数据。不管 mock 做得多完美交付前一定连一次真实接口重点看三个地方字段名、空值形态、时间格式。我见过太多“本地全对、联调全崩”的情况根因都是 mock 数据太理想化了——真实的接口不会每次都返回你期望的那个形状它会返回 null、返回空字符串、返回超出你预期的长度。提前用真实数据压一遍比事后救火划算得多。心得六别把 mock 当成长期方案。它的价值在于让开发不停摆而不是替代后端。当一个接口的 mock 规则改了三次以上说明契约本身不稳定这时候该做的是拉着后端把字段定下来而不是继续在前端维护一份越来越复杂的假数据。我个人判断的标准是mock 规则的生命周期超过两周还没对齐就该升级成契约同步的问题了。最后再分享一个小技巧。如果项目里 mock 规则比较多可以在 handler 里加一个统一的前置日志把匹配到的路径、方法、查询参数打出来。这样当某个接口行为异常时你一眼就能看出请求到底有没有被拦截、参数是什么形状比在浏览器里一层层翻 Network 面板快得多。这套日志在开关关闭后不会被加载也不会有任何线上开销。
返回列表