上周三下午,产品经理在群里甩过来一张原型图,说这个页面下周一要给客户演示。我看了眼接口文档,后端同事那边表结构还在改,接口最快也得下周三才能出第一版。这种场景做前端的应该都不陌生——布局、交互、样式、动画全都能自己搞定,唯独数据卡在别人手里。这时候前端接口模拟就不是什么锦上添花的加分项,而是决定你能不能按时下班的硬需求。Apifox 这类工具的价值就在这儿:在接口还不存在的时候,先把一套看起来足够真实的响应数据造出来,前端代码照常写、照常调;等后端接口真的好了,改一行配置就能切过去,业务代码一行不用动。
我自己这套流程前后迭代了大概三四个版本,从一开始用死数据写死在 js 文件里,到后来用本地 json-server,再到把 Mock 数据统一收拢到 Apifox 里管理。中间踩的坑不算少,但也确实总结出了一些能复用的做法。下面这些东西适合两类人看:一类是刚接触前后端分离、还不太清楚接口模拟该怎么下手的同学;另一类是已经在用类似工具、但总觉得团队协作和后期联调环节有点别扭的老手。不管你用的是 Vue、React 还是别的框架,思路都是通的。
1. 前端接口模拟到底在解决什么问题
很多人对"接口模拟"的理解停留在"造点假数据让页面能显示出来",这个认知其实把它的价值压得太低了。模拟接口真正解决的是开发节奏的解耦——前端和后端的进度不再强绑定,两边可以并行往前推。这件事听起来简单,但它在实际项目里带来的收益,往往比多写两个组件大得多。
1.1 等接口的代价:一个被低估的时间黑洞
先把账算清楚。一个中等规模的后台页面,涉及列表查询、详情、新增、编辑、删除,加上各种下拉选项和统计卡片,少说也有六到八个接口。如果老老实实等后端出接口,从后端写完、自测、联调、改 bug 到真正可用,快的话三天,慢的话拖一周很正常。这一周里前端干什么?只能写静态页面、抠样式、调动画,然后拿着一堆// TODO 等接口的注释发呆。
更麻烦的是,前端在写静态页面的时候,是不知道数据结构长什么样的,于是组件的 props 只能先随便定一个形状。等接口来了发现字段对不上,又得回头重构组件。这一来一回,浪费的就不只是等待的时间,还有返工的时间。
我的做法是:拿到原型图和需求的第一时间,就把接口契约先"假定"出来。字段名怎么起、返回结构是平铺还是包一层data、分页参数叫page还是pageNum,这些先定下来,然后立刻在 Apifox 里把接口建好、Mock 数据配好。前端从第一天就能拿到结构稳定的数据,组件写完基本不用改。后端那边看到这份契约,也能少走很多弯路——因为字段语义已经被讨论过了。
1.2 手写假数据的三种做法,为什么最后都会失控
在引入专门的工具之前,大家基本都是这几种土办法,我挨个说说它们的寿命有多长。
第一种是直接在组件里写死数据。初期最省事,const list = [{ id: 1, name: '测试' }]扔进去就完事。问题是数据一旦需要在多个组件之间共享,就得复制粘贴;想模拟加载状态和错误状态,还得再加一堆开关变量;最后这个文件会变成一坨谁都不敢删的垃圾,联调时清理它的成本比写它还高。
第二种是在项目里放一个 mock 目录,手动拦截请求。比第一种进步了不少,至少请求路径是真实的。但问题在于维护成本:每个接口都要写一份假数据,字段变了要同步改,分页逻辑要自己实现一遍,时间久了和真实接口的差异越来越大,最后大家都不信任这份 Mock 数据了。
第三种是跑一个本地 json-server。这个方案我用了挺久,它把数据放进一个 json 文件里,支持简单的 REST 路由和分页查询,确实好用。但它有几个硬伤:一是没法方便地模拟延迟、异常码、随机数据;二是团队协作时 json 文件要提交到仓库,冲突不断;三是它和接口文档是两套东西,改了一边忘了另一边。
这三种做法的共同问题是:Mock 数据和接口定义是分离的。而分离就意味着一定会漂移。
1.3 Apifox 在整条开发链路里的位置
Apifox 的思路是把接口文档、Mock 服务、调试、测试放在同一个地方。你在里面定义一个接口,这个定义同时承担了三个角色:给后端看的文档、给前端用的 Mock 数据源、给测试用的请求模板。改一处,三处同时生效。
这一点非常关键。当接口定义成为唯一的真相来源时,"假数据"和"真接口"之间就不存在结构差异了——因为 Mock 响应本身就是根据接口定义里的数据结构生成的,字段名、类型、层级关系全都一致。这也是它比 json-server 那类方案更省心的地方。
顺便提一句,它支持从浏览器里把已经跑通的请求直接同步进项目。这个功能我平时用得挺多:前端联调时抓到一个真实响应,一键同步过去,Schema 就自动补全了,比自己照着响应手敲一遍快得多,也不容易敲错字段名。
2. 动手之前的项目结构与环境规划
工具用得好不好,一半取决于前期结构规划。我见过太多项目,接口建了三百个,全堆在一个根目录下,三个月后连作者自己都要靠搜索才能找到。这部分讲的都是"防未来"的事,看着啰嗦,但真能省下后面的时间。
2.1 客户端选择:桌面版、Web 版与团队协作的取舍
Apifox 提供了客户端和网页版两条路。我的建议是这样:
个人开发阶段用客户端就够了。响应快、支持本地环境变量、不用每次登录,离线也能看文档。本地 Mock 服务跑起来之后,前端项目直接指向本地端口就行。
团队协作阶段就得考虑把数据放到云端。理由很简单:接口定义是团队的公共资产,不能存在某一个人的电脑里。新人入职第一天要能看到所有接口文档,后端改了字段要能推送给前端,这些都需要一个共享的存储。这时候用账号体系把项目挂到云端,或者用团队版的组织结构来管理,会顺畅很多。
有一点要注意:如果你所处的环境对数据外发有要求,那么所有接口定义、示例数据都不要放云端,老老实实本地自建。这一点在项目启动前就要确认清楚,别等接口建了一百个再迁移。
2.2 目录分组怎么切,才能避免三个月后自己都找不到接口
目录结构的切法没有标准答案,但有几个原则我一直在用。
按业务模块切,不要按 HTTP 方法切。有些人喜欢建GET、POST这种分组,纯属自找麻烦——真实的业务查询往往横跨多种方法,按方法切等于没切。
目录层级控制在三层以内。比如业务域 / 子模块 / 接口,再深就该考虑拆项目了。层级过深的后果是点开一个接口要点四五个来回,效率极低。
公共接口单独拉一个分组。登录、上传、字典查询、消息通知这类跨模块共用的接口,单独放在一个公共分组下,避免在每个业务模块里重复建一遍。
命名用中文还是英文?我的经验是目录用中文,接口路径用英文。目录是给人看的,中文一眼就懂;路径是要写进代码的,必须英文并且符合规范。混着来反而容易乱。
给你一个我实际用过的结构参考:
| 层级 | 示例 | 说明 |
|---|---|---|
| 一级 | 订单中心 | 业务域 |
| 二级 | 订单管理 | 子模块 |
| 三级 | 订单列表 / 订单详情 / 订单取消 | 具体接口 |
| 独立 | 公共 / 用户 / 字典 / 文件 | 跨模块共用 |
2.3 环境变量与前置 URL 的配置思路
这是最容易被忽略、但收益最高的一个配置。核心思路是:接口路径里永远不要写完整的域名。
正确的做法是把http://localhost:4523或者http://10.0.0.21:8080这种前缀抽成环境变量,比如命名为baseUrl,然后在每个接口的路径里写{{baseUrl}}/api/order/list。这样切换环境时只改一个变量,所有接口跟着变。
我一般会建这么几套环境:
- 本地 Mock:
baseUrl指向 Apifox 本地 Mock 服务的地址 - 测试环境:指向后端的测试服务器
- 预发环境:指向预发服务器
- 生产环境:只读,主要用于查看真实响应结构,不要在这上面发请求
配好之后,前端同学只要知道当前该用哪套环境就行,不用去猜接口地址。这个习惯养成了,后面从 Mock 切真实接口就是点一下的事。
3. 从接口定义到 Mock 数据落地的完整过程
这一节是整篇的核心。我按实际操作顺序来讲,每一步都会说明为什么这么做,以及有哪些细节容易漏。
3.1 先把接口契约写清楚:路径、方法、字段语义
新建接口的时候,几个字段必须认真填:
路径要和最终真实接口保持一致,包括前缀。别图省事写成/list,等联调时发现真实路径是/api/v1/order/list,又得全量改一遍。
请求方法要对。查询用GET,新增用POST,全量更新用PUT,局部更新用PATCH,删除用DELETE。虽然 Mock 阶段方法写错也能跑通,但前端封装请求的时候是按方法来的,写错了后面会很别扭。
请求参数要区分清楚query、path、body、header四类。我见过不少人把分页参数塞进 body,结果真实接口在 query 里,联调时全乱。
返回结构要先定下来。这里有个团队约定问题:是直接返回数组,还是包一层{ code, message, data }?我的建议是统一包一层,因为真实项目里总会有业务状态码、错误提示这些需求,早点定下来比后面改要轻松得多。
字段命名风格也要统一。全驼峰还是全下划线,在一开始就要说清楚。这个坑我踩过——前端按驼峰写,后端按数据库字段名返回下划线,联调时对着一个userId和一个user_id查了一下午。
3.2 数据结构的 Schema 描述与示例值填写
接口定义好之后,接下来要描述数据结构。Apifox 里可以直接用 JSON 示例反推 Schema,也可以手动定义字段类型。
我的习惯是先手写一份最完整的响应示例,把所有可能出现的字段都写进去,包括那些正常情况下可能为空的。然后反推成 Schema,再回头检查类型。
几个容易漏的类型细节:
- 数字和字符串要分清。订单号这种很长的数字,如果按 number 返回,前端在 JS 里会有精度问题。这类字段统一按 string 处理。
- 布尔值别用 0/1 凑合。有些后端喜欢返回
status: 1,前端就得写一堆status === 1的判断。用true/false表达状态,可读性高很多。 - 时间格式要统一。全用时间戳,或者全用
YYYY-MM-DD HH:mm:ss,别一半一半。 - 枚举值要列出来。状态字段有哪些取值,各自代表什么含义,写在字段描述里。这个描述后面会直接给前端当参考。
注意:Schema 里字段的"是否必填"要如实标注。全标必填的话,前端就会写一堆不必要的空值判断;全不标的话,又容易漏掉空数据场景。按真实业务来判断。
3.3 Mock 规则语法:常用占位符与真实场景对照
接下来就是让数据"动起来"。Apifox 的 Mock 语法基于一套常见的占位符规则,在字段的示例值里写@xxx就能自动生成。
我把常用的归了个类,这张表是我自己整理后一直在用的:
| 占位符 | 效果 | 典型用途 |
|---|---|---|
@cname | 随机中文姓名 | 用户昵称、联系人 |
@integer(1, 100) | 指定范围整数 | 数量、库存、评分 |
@float(0, 1000, 2, 2) | 指定范围浮点数 | 金额、重量 |
@datetime('yyyy-MM-dd') | 日期 | 创建时间、订单日期 |
@now | 当前时间 | 更新时间 |
@city | 城市名 | 地址、门店 |
@guid | 全局唯一标识 | 主键 ID |
@image('200x100') | 图片地址 | 头像、封面 |
@boolean | 布尔值 | 启用状态 |
@pick(['待付款','已发货']) | 从数组里挑一个 | 状态枚举 |
有了这张表,基本八成的字段都能造出来。剩下两成需要点技巧,比如"订单号要以 ORD 开头后面跟八位数字",这种就得用字符串拼接的方式写:ORD@integer(10000000, 99999999)。
再举几个我实际项目里用过的写法:
// 手机号:用固定前缀加随机数字,看起来更像真的 "1@integer(3,9)@integer(100000000, 999999999)" // 金额:保留两位小数,范围控制在合理区间 "@float(9.9, 9999, 2, 2)" // 头像:用固定尺寸的图片占位 "@image('120x120', '#f5f5f5', '头像')" // 嵌套数组:配合数组长度控制,一次生成五个元素 "@integer" // 用在数组长度字段上关于数组这块要多说两句。Apifox 里控制返回数组长度的方式是在数组字段的类型设置里指定长度规则,可以固定长度,也可以给一个范围让它随机。我一般会设成3-8这种范围,这样每次刷新页面看到的数据条数都不一样,更接近真实。
3.4 让假数据"经得起推敲"的几组高级写法
基础的随机占位符只能保证数据"不重复",但离"像真的"还有距离。真正让人觉得数据真实的,是数据之间的逻辑关联。举几个例子。
场景一:状态和按钮的关联。一个订单列表里,如果所有订单都是"待付款",那前端做的那些状态切换逻辑根本测不到。这时候可以用@pick从多个状态里随机取,让每条数据的状态都不一样。但要注意,状态字段和操作按钮字段最好能对应上——如果设计了canCancel这种字段,它的值应该和status一致。
场景二:列表和详情的关联。列表里返回的 id,点进详情应该能查到对应数据。如果 Mock 完全是随机的,那详情页永远是随机数据,前端就没法验证"从列表跳详情"这个流程。解决办法是把列表和详情的 Mock 都用同一套数据结构,并且保证 id 字段的生成规则一致。
场景三:金额的计算关系。一个订单里有单价、数量、总价,如果这三个字段都是独立随机的,那 2 件单价 50 的商品总价显示 3287,一眼就假。这种情况要么用固定的示例数据,要么用 Mock 脚本按公式计算。Apifox 支持在响应里写脚本,根据前面的字段算出后面的字段。
场景四:时间顺序。创建时间应该早于更新时间,如果两个都用@now,就会出现"创建时间和更新时间一模一样",甚至更新时间更早的情况。这种情况我一般会让创建时间往前偏移几天,更新时间用当前时间。
这几组写法说白了就一句话:假数据的价值不在于字段填满,而在于它能否触发你想要测试的分支。
4. 前端工程侧怎么把 Mock 接进真实代码
Mock 服务配好了,接下来是前端项目这边怎么接。这一步做得好,后面切真实接口几乎零成本;做得不好,联调时会痛苦得想重写。
4.1 环境切换方案:代理转发与请求层拦截
主流方案有两种,我分别说说适用场景。
方案一:开发服务器代理转发。在项目的构建配置里配置代理,把/api开头的请求转发到 Mock 服务的地址。Vue 项目在vue.config.js或vite.config.js里配,React 项目在对应的配置里配。优点是业务代码完全无感,请求路径保持不变,切环境只改配置。
// vite.config.js 里的代理配置示例 export default defineConfig({ server: { proxy: { '/api': { target: 'http://127.0.0.1:4523/m1/xxxxx', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } })方案二:请求层拦截。在封装的请求函数里判断当前环境,如果是 Mock 环境就走本地假数据,否则走真实请求。这种方式灵活度高,但需要前端额外维护一份假数据,又回到了前面说的"数据分离"问题。
我的选择是方案一为主,方案二只用于极端情况。比如某个接口需要模拟网络超时,而 Mock 服务没法方便地配置,这时候才在前端做局部拦截。
4.2 请求封装的设计:让 Mock 和真实接口走同一条路
无论用哪种方案,请求封装的抽象层一定要做好。核心原则是:业务代码里不要出现任何 Mock 相关的判断。
我一般会这样分层:
- 最底层是基础的请求库封装,处理拦截器、错误码、loading 状态
- 中间层是按业务模块划分的接口函数,比如
getOrderList(params) - 最上层是组件调用
Mock 和真实接口的切换发生在最底层,通过环境变量或者代理配置来决定。中间层和上层完全不知道数据从哪来。
这样做的好处是,联调的时候只需要改一个环境变量的值,整个项目的数据源就切过去了。如果发现某个接口有问题,切回来继续用 Mock,不影响其他模块的开发。
有个细节值得注意:请求超时和错误处理要提前设计好。很多人在 Mock 阶段从不模拟错误,导致错误提示的 UI 从来没被触发过,联调时突然报错,页面直接白屏。建议在 Mock 阶段就配几个会返回错误的接口,把错误分支走一遍。
4.3 跨域、鉴权头、文件上传这些特殊请求怎么处理
这三类请求在 Mock 阶段经常出问题,我逐个说。
跨域问题。Mock 服务通常跑在另一个端口上,直接请求会触发跨域。用代理转发就没这个问题,因为浏览器看到的是同源请求。如果不用代理,就得让 Mock 服务开启跨域支持——Apifox 的 Mock 服务默认是支持的,但如果你在请求里带了自定义头,可能会触发预检请求,需要确认一下。
鉴权头。真实接口一般需要在请求头里带 token。Mock 阶段有两种做法:一是在 Mock 服务里配置不校验鉴权,前端随便传;二是在前端请求拦截器里统一注入一个固定的假 token。我倾向后者,因为这样能顺便验证拦截器的逻辑是否正确,等切换到真实接口时只需要替换 token 的取值。
文件上传。这是最容易翻车的地方。普通的 Mock 只能返回 JSON,而上传接口的响应结构、上传进度、失败重试这些逻辑都需要专门测试。我一般会把上传接口单独处理:Mock 服务返回一个固定的文件地址,前端这边用一个真实的本地图片走一遍完整流程,验证进度条和回调是否正常。
提示:文件上传的 Mock 千万别只测成功路径。断网、文件过大、格式不对这三种情况,在真实环境里发生的概率比你想的高得多。
5. 踩过的坑:Mock 数据用着舒服,上线却翻车
这部分是我最想写的。前面讲的都是"怎么做对",这里讲的是"哪里会错"。这些坑的共同特点是:Mock 阶段一切正常,联调或上线时才暴露,排查起来很费时间。
5.1 数据太干净:分页、空数组、异常码没人造
最典型的翻车场景是这样的:Mock 数据里分页永远返回 10 条,前端列表页写得好好的。上线后真实数据只有 3 条,或者某个分类下一条都没有,页面就出问题了——要么空状态没设计,要么分页组件崩了。
还有一种更隐蔽的情况:总页数计算。Mock 里固定返回 10 条数据、总数 100,前端算出来 10 页,逻辑看起来没问题。真实场景下如果总数是 0,有些分页组件的实现会除以零,直接报错。
我的做法是在 Mock 阶段就把这三种情况都造一遍:
- 正常数据:按预期返回完整列表
- 空数据:返回空数组,总数 0
- 边界数据:只有 1 条数据,或者刚好是分页大小的整数倍
具体怎么切?最省事的办法是在接口路径上加个参数,比如?scene=empty,然后在 Mock 规则里根据这个参数返回不同的数据。这样测试的时候改一下参数就能切换场景,不用反复改配置。
异常码也是一样。400、401、403、500这几种状态码,前端应该都有对应的处理逻辑,但如果不主动造出来,这些逻辑就永远没被验证过。我一般会特意配一个/api/debug/error接口,专门返回各种错误状态,用来测试错误提示和登录过期跳转。
5.2 字段漂移:后端改了字段名,前端一无所知
这是我踩过最深的坑。Mock 阶段前端按userName写,后端实现时用了username,联调时前端拿不到值,排查了半天才发现大小写不一致。
这个问题的根源是 Mock 和真实接口是两套实现。解决办法只有一个:让接口定义成为唯一来源。后端在实现时应该以接口定义为准,如果确实需要改,必须先在接口定义里改,然后通知前端。
实际操作中,我会做两件事来降低风险:
第一,在请求层加一层字段校验。开发环境下,如果响应的字段结构和接口定义对不上,控制台直接报警告。这个可以用简单的 Schema 校验库实现,成本不高但收益很大。
第二,联调前做一次字段 diff。把真实接口的响应和 Mock 的响应各跑一遍,对比字段列表。这一步手动做也就几分钟,但能提前发现大部分漂移问题。
5.3 循环调用与接口依赖链的模拟思路
有些业务场景里,接口之间是有依赖的。比如列表页的数据依赖于用户权限接口返回的角色信息,或者一个详情页需要先拿到配置再请求数据。这类链路在 Mock 阶段很容易被简化成"直接返回最终结果",结果联调时才发现调用顺序有问题。
我的处理思路是把依赖关系显式化:
- 能独立 Mock 的接口就独立 Mock,不要在一个接口里返回其他接口的数据。
- 有先后依赖的接口,在文档里标注清楚调用顺序。Apifox 里可以用接口描述来写,也可以在文档目录里按顺序排列。
- 需要循环调用的场景,比如分批拉取数据、轮询任务状态,要单独设计。这类接口的特点是同一个路径会被多次调用,返回的数据应该逐次变化。这时候可以在 Mock 脚本里维护一个计数器,每次调用返回不同的内容。
轮询场景还有个小技巧:可以让 Mock 服务在若干次调用后返回"完成"状态,否则前端会一直轮询下去,测试时页面卡在 loading 状态出不来。
5.4 时间与随机性带来的不可复现问题
Mock 数据的一个副作用是不可复现。每次刷新页面数据都不一样,这本来是好事,但排查问题时就成了麻烦——你没法确定刚才那个 bug 是不是因为某条特定数据触发的。
我遇到过两次这种情况:一次是某个订单金额恰好是整数,导致金额格式化的逻辑显示异常(少了小数点后两位);另一次是用户姓名恰好只有两个字,把某个布局撑歪了。
解决办法是关键场景用固定数据,非关键场景才用随机数据。判断标准很简单:如果一个字段会参与样式计算、格式转换或者逻辑判断,那它就应该用固定值或者受限的取值集合;如果只是用来填充列表让它看起来丰富,用随机值就没问题。
6. 从 Mock 到联调的协作闭环
前面讲的都是技术层面的东西,这一节讲讲人和流程。接口模拟这件事,技术只是基础,真正决定效率的是前后端怎么配合。
6.1 前后端共用一份契约:变更同步的实际做法
理想状态是:前端和后端在同一个接口定义上工作,任何变更都通过这份定义来同步。实际操作中要落地这件事,需要几个约定。
约定一:接口定义由谁维护。我的建议是谁先用到谁维护,但最终以后端为准。前端在 Mock 阶段把接口定义建起来,后端实现时如果发现定义有问题,直接在定义上改,并知会前端。不要出现两边各改各的情况。
约定二:变更要走通知流程。字段增删改,必须提前说。这句话听起来像废话,但实际上很多团队的沟通就卡在这儿——后端觉得"改个字段名而已",前端改起来可能要动好几个组件。我的做法是在团队里约定:接口定义的任何修改,都要在群里发一条消息,格式统一为"接口路径 + 变更内容 + 影响范围"。
约定三:用版本或者标签标记接口状态。哪些接口还是 Mock,哪些已经联调通过,状态要清晰。Apifox 里可以用目录或者自定义字段来标记,团队里一眼就能看出来当前进度。
6.2 导出、分享与自动化测试的衔接
接口定义建好之后,其实还有不少可以复用的地方,很多人只用了 Mock 这一个功能就有点浪费了。
导出成文档。可以导出成 Markdown 或者 HTML,直接发给需要接口文档但没账号的同事,比如产品或者测试。这个我用得挺多,产品每次要接口列表,我就导一份给他。
导出成表格。有些场景下需要把接口清单整理成 Excel,比如做工作量评估或者归档。这种一键导出的功能比手动整理快得多。
衔接自动化测试。接口定义里的请求参数和示例值,可以直接拿来做接口测试的用例基础。测试同学如果想做接口自动化,从这份定义起步比从零写脚本省事很多。
给新人的价值。一个维护良好的接口定义库,本质上就是一份活的接口文档。新人入职时不用到处问"这个接口怎么调",打开看一眼就清楚了。这一点在人员流动频繁的团队里价值特别高。
6.3 切换到真实接口前的自检清单
联调那天最容易手忙脚乱,我整理了一份自检清单,每次切环境前过一遍,能省掉不少来回。
| 检查项 | 检查内容 | 常见问题 |
|---|---|---|
| 请求路径 | 前缀和路径与真实接口一致 | Mock 路径少了/api/v1 |
| 请求方法 | GET/POST 与真实接口一致 | 查询用了 POST |
| 参数位置 | query/body/path 位置正确 | 分页参数塞在 body 里 |
| 字段命名 | 前后端风格统一 | 驼峰与下划线混用 |
| 数据类型 | 数字/字符串/布尔对应 | 长数字精度丢失 |
| 空值处理 | 空数组、null、0 的处理 | 把 0 当成空值 |
| 错误码 | 各状态码的处理逻辑 | 401 没做跳登录 |
| 鉴权 | token 的注入和刷新 | 请求头字段名写错 |
| 分页 | 总数为 0、总页数计算 | 除以零报错 |
| 时间格式 | 前后端格式一致 | 时间戳与字符串混用 |
这份清单看起来琐碎,但每一条我都真实踩过。特别是"把 0 当成空值"这一条——if (!count)这种写法,当接口返回 0 的时候会走错分支,静态检查还查不出来。
最后再分享一个小技巧:联调时保留 Mock 环境作为兜底。不要一上来就把 Mock 配置删了,而是把环境变量切成真实接口,遇到某个接口不通、又想继续开发其他功能的时候,把环境切回 Mock 就能接着干。两边来回切几次,比干等着后端修接口快得多。
这套流程我用了大半年,最大的感受是:工具本身不难,难的是把它变成团队的习惯。接口定义这件事,只要有一个环节的人不当回事,整个链路就会重新退回到"各写各的"状态。所以真正值得投入的,其实是把那几条约定说清楚、坚持住,工具只是把这些约定具象化了而已。