1. 项目概述
在前后端分离的开发模式下,Mock数据已经成为现代Web开发不可或缺的基础设施。作为一名经历过多次前后端联调"拉锯战"的前端工程师,我深知一套好的Mock工具对开发效率的提升有多重要。传统的Mock方案要么数据质量差,要么配置繁琐,要么无法团队共享,导致开发过程中经常出现"假数据联调通过,真数据一堆问题"的尴尬局面。
经过多次实践和优化,我们团队开发了一套基于AI生成高质量Mock数据的工具,核心解决了四个关键问题:
- 无缝请求拦截:不侵入业务代码,自动拦截XHR和Fetch请求
- 智能规则匹配:支持多参数、多场景的精细化匹配
- 高质量数据生成:基于接口文档自动生成符合业务语义的数据
- 团队协作共享:支持本地、个人云端和团队三级数据共享
这套工具已经在公司内部多个项目中落地使用,平均为每个项目节省了30%以上的联调时间,特别是对于复杂业务场景的覆盖度提升了近80%。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 业界常见方案对比
在开发这套工具前,我们调研了市面上主流的Mock方案,发现各有优缺点:
2.1 硬编码Mock
直接在业务代码中写死返回数据:
javascript复制function getUserInfo() {
if (process.env.NODE_ENV === 'development') {
return Promise.resolve({
name: '测试用户',
age: 18
})
}
return fetch('/api/user')
}
优点:实现简单,无需额外工具
缺点:
- 污染业务代码,发布前需要手动删除或切换
- 无法模拟网络延迟、HTTP状态码等真实场景
- 修改数据需要重新编译项目
2.2 Mock.js
通过拦截AJAX请求并生成随机数据:
javascript复制Mock.mock('/api/user', {
'name': '@cname',
'age|18-60': 1
})
优点:
- 内置丰富的数据模板语法
- 支持正则表达式匹配URL
缺点: - 生成的数据缺乏业务语义(如状态码、ID关联等)
- 复杂场景配置繁琐
- 无法团队共享配置
2.3 代理工具(如whistle)
通过本地代理服务器拦截和修改请求:
code复制pattern enable://mock
pattern mock://{pathToMockFile}
优点:
- 不侵入业务代码
- 支持热更新
缺点: - 配置分散在不同文件中
- 无法根据请求参数动态返回不同数据
- 团队成员需要各自配置
2.4 MSW(Mock Service Worker)
基于Service Worker的API Mock库:
javascript复制import { setupWorker, rest } from 'msw'
const worker = setupWorker(
rest.get('/user', (req, res, ctx) => {
return res(
ctx.delay(150),
ctx.json({ name: 'John' })
)
})
)
优点:
- 网络层拦截,更接近真实场景
- 支持模拟网络延迟、错误等
缺点: - 需要注册Service Worker
- 生产环境需要额外处理
- 配置复杂度较高
经过对比,我们发现现有方案都无法同时满足以下四个核心需求:
- 低侵入:不修改业务代码,生产环境自动禁用
- 规则灵活:支持基于URL、参数、Header等的精细匹配
- 数据质量高:生成符合业务语义的数据,而不仅是随机数据
- 团队可共享:配置可以云端同步,而非仅存在本地
3. 系统架构设计
3.1 整体架构
code复制┌───────────────────────────────────────┐
│ 业务项目 │
│ import { mockInit } from '@zz-common/ai_mock' │
│ mockInit({ rules: ['api.example.com'] }) │
└──────────────────────┬────────────────┘
│
┌──────────────────────▼────────────────┐
│ ai_mock (npm 包) │
│ XHR/Fetch拦截 → 规则匹配 → 返回Mock数据 │
└──────────────────────┬────────────────┘
│ CustomEvent通信
┌──────────────────────▼────────────────┐
│ mock-sdk (可视化面板) │
│ 请求列表 | 规则管理 | 数据编辑器 │
└──────────────────────┬────────────────┘
│ HTTP
┌──────────────────────▼────────────────┐
│ node (后端服务) │
│ 接口文档获取 → AI生成 → 数据持久化 │
└───────────────────────────────────────┘
3.2 核心设计原则
-
职责分离:
ai_mock仅负责请求拦截和规则匹配,不包含UI代码mock-sdk通过CDN动态注入,不增加业务包体积
-
松耦合通信:
- 核心库与面板通过
CustomEvent通信 - 避免直接依赖,方便独立升级
- 核心库与面板通过
-
环境感知:
- 通过
process.env.NODE_ENV自动判断环境 - 生产环境完全禁用,避免性能开销
- 通过
-
性能优化:
- 规则数据使用
WeakMap缓存 - 大量请求时启用批量处理
- 规则数据使用
4. 核心实现细节
4.1 请求拦截实现
现代前端应用混合使用XMLHttpRequest和fetch,需要同时拦截两种请求方式。
4.1.1 XHR拦截
通过重写XMLHttpRequest.prototype.send实现:
javascript复制const originalXHRSend = XMLHttpRequest.prototype.send
const isRealRequest = new WeakMap()
XMLHttpRequest.prototype.send = function(...args) {
const xhr = this
const url = getRequestUrl(xhr)
// 防止递归标记
if (isRealRequest.get(xhr)) {
return originalXHRSend.apply(xhr, args)
}
// 检查Mock规则
if (shouldMock(url)) {
const mockData = getMockData(url)
applyMockResponse(xhr, mockData)
// 后台发送真实请求用于对比
const realXhr = cloneXHR(xhr)
isRealRequest.set(realXhr, true)
originalXHRSend.call(realXhr, ...args)
return
}
originalXHRSend.apply(xhr, args)
}
4.1.2 Fetch拦截
通过重写window.fetch实现:
javascript复制const originalFetch = window.fetch
window.fetch = async function(input, init) {
const url = typeof input === 'string' ? input : input.url
if (shouldMock(url)) {
const mockData = getMockData(url)
return Promise.resolve(
new Response(JSON.stringify(mockData.data), {
status: mockData.status,
headers: mockData.headers
})
)
}
return originalFetch(input, init)
}
4.1.3 关键问题解决
- 只读属性覆写:
javascript复制function applyMockResponse(xhr, data) {
Object.defineProperties(xhr, {
readyState: { get: () => 4 },
status: { get: () => data.status || 200 },
responseText: {
get: () => typeof data === 'string' ? data : JSON.stringify(data)
}
})
// 触发事件
xhr.dispatchEvent(new Event('readystatechange'))
xhr.dispatchEvent(new Event('load'))
}
- 防止递归:
- 使用
WeakMap标记真实请求 - 克隆XHR对象时排除事件监听器
- 第三方库兼容:
- 保留原始方法引用
- 确保事件触发顺序符合标准
4.2 规则匹配引擎
4.2.1 规则数据结构
typescript复制interface MockRule {
id: string;
url: string; // 接口URL或正则
name: string; // 规则名称
priority: number; // 优先级
enabled: boolean; // 是否启用
conditions: Condition[]; // 匹配条件
response: any; // 响应数据
delay?: number; // 延迟(ms)
}
interface Condition {
source: 'query' | 'body' | 'header' | 'cookie' | 'path';
key: string;
operator: '=' | '!=' | '>' | '<' | 'contains' | 'regex';
value: any;
}
4.2.2 匹配算法流程
- 过滤:只处理已启用的规则
- 排序:按优先级从高到低排序
- 匹配:找到第一个完全匹配的规则
- 应用:返回对应的响应数据
javascript复制function matchRule(request) {
const enabledRules = rules
.filter(rule => rule.enabled)
.sort((a, b) => a.priority - b.priority)
for (const rule of enabledRules) {
if (matchUrl(rule.url, request.url) &&
matchConditions(rule.conditions, request)) {
return rule
}
}
return null
}
4.2.3 条件匹配实现
javascript复制function matchConditions(conditions, request) {
return conditions.every(cond => {
const value = getParamValue(cond.source, cond.key, request)
switch (cond.operator) {
case '=': return value == cond.value
case '!=': return value != cond.value
case '>': return value > cond.value
case '<': return value < cond.value
case 'contains': return String(value).includes(cond.value)
case 'regex': return new RegExp(cond.value).test(value)
default: return false
}
})
}
4.3 AI数据生成
4.3.1 数据生成流程
- 获取接口文档:从Swagger/YAPI等平台获取JSON Schema
- 构建Prompt:根据字段描述和业务语义生成提示词
- 调用AI服务:发送请求获取生成结果
- 数据校验:验证JSON格式和字段完整性
4.3.2 核心Prompt设计
text复制你是一个专业的Mock数据生成器,请根据以下JSON Schema生成符合业务语义的测试数据。
【生成规则】
1. 优先使用description中的枚举值(如"0:成功,1:失败"生成0或1)
2. 根据字段名推断合理值:
- name => 中文姓名
- age => 18-60的整数
- price => 保留2位小数的浮点数
3. 数组类型默认生成3-5条数据
4. 关联字段保持逻辑一致(如userId和userInfo.id相同)
【输出要求】
1. 仅返回JSON,不要解释
2. 保持字段类型与Schema一致
3. 必填字段不能为空
【示例Schema】
{
"user": {
"id": "string // 用户ID",
"name": "string // 用户名",
"type": "number // 0:普通用户 1:VIP用户"
}
}
4.3.3 生成效果对比
传统Mock.js生成:
json复制{
"code": 0,
"data": {
"id": "123",
"name": "李四",
"type": 1
}
}
AI生成(基于业务语义):
json复制{
"code": 0,
"message": "success",
"data": {
"id": "user_123456",
"name": "张伟",
"type": 1,
"vipExpire": "2024-12-31",
"discountRate": 0.9
}
}
4.4 团队协作方案
4.4.1 三级存储结构
-
本地存储:IndexedDB,用于临时调试
- 特点:仅当前设备可用
- 场景:快速验证想法
-
个人云端:远程数据库,用户私有
- 特点:跨设备同步
- 场景:个人常用测试用例
-
团队共享:远程数据库,全员可见
- 特点:统一管理
- 场景:标准测试数据、边界用例
4.4.2 数据同步策略
mermaid复制graph TD
A[本地修改] -->|手动发布| B(个人云端)
B -->|申请发布| C(团队共享)
C -->|审核通过| D[全员可见]
D -->|订阅更新| A
4.4.3 冲突解决机制
- 优先级:团队 > 个人 > 本地
- 合并策略:
- 新增规则:直接合并
- 修改冲突:保留最新版本
- 删除操作:标记为禁用而非物理删除
5. 使用指南
5.1 快速开始
- 安装依赖:
bash复制npm install @zz-common/ai_mock
- 项目初始化:
javascript复制import { mockInit } from '@zz-common/ai_mock'
mockInit({
rules: ['api.example.com'],
exclude: [/\.(png|jpg)$/, /static/],
debug: true
})
- 访问面板:
javascript复制// 默认通过快捷键Ctrl+Shift+M打开
// 或手动触发
window.__MOCK_TOOL.togglePanel()
5.2 典型工作流
-
捕获请求:
- 正常操作页面
- 在面板中查看捕获的请求列表
-
创建规则:
- 点击"Create Mock"按钮
- 设置URL匹配规则(支持通配符和正则)
-
配置响应:
- 手动编辑JSON
- 或点击"AI Generate"自动生成
-
参数化匹配:
javascript复制// 当query包含type=1时返回VIP数据 { "conditions": [{ "source": "query", "key": "type", "operator": "=", "value": "1" }], "response": { "code": 0, "data": { "userType": "VIP", "discount": 0.8 } } } -
团队共享:
- 将稳定规则发布到团队库
- 新成员安装即可获得全部测试用例
5.3 高级功能
- 动态模板:
json复制{
"requestId": "{{uuid()}}",
"userId": "{{request.query.userId}}",
"currentTime": "{{Date.now()}}"
}
- 异常模拟:
javascript复制{
"delay": 2000, // 延迟2秒
"status": 500, // 模拟服务器错误
"response": {
"code": -1,
"message": "服务不可用"
}
}
- 数据关联:
javascript复制// 用户查询返回的ID可用于订单查询
{
"conditions": [{
"source": "path",
"key": "userId",
"operator": "=",
"value": "{{prevResponse.userId}}"
}]
}
6. 性能优化
6.1 缓存策略
- 规则缓存:使用WeakMap缓存已匹配规则
- 请求缓存:相同参数请求返回缓存结果
- 懒加载:按需加载AI生成模块
6.2 内存管理
- 请求清理:自动清理过期的请求记录
- 缓存限制:单接口最多缓存100条规则
- 垃圾回收:定时清理未使用的引用
6.3 生产环境处理
- 自动禁用:
process.env.NODE_ENV === 'production' - 代码移除:通过babel插件在构建时移除Mock代码
- 体积分析:开发模式注入,生产环境CDN加载
7. 实践案例
7.1 电商项目
场景:商品详情页需要模拟:
- 正常商品
- 缺货商品
- 秒杀商品
- 下架商品
解决方案:
javascript复制// 规则1:正常商品
{
"conditions": [{
"source": "path",
"key": "skuId",
"operator": "regex",
"value": "^\\d+$"
}],
"response": { /* 正常数据 */ }
}
// 规则2:秒杀商品
{
"conditions": [{
"source": "query",
"key": "type",
"operator": "=",
"value": "flashsale"
}],
"response": { /* 秒杀数据 */ }
}
7.2 金融项目
需求:根据用户风险等级返回不同理财产品
实现:
javascript复制{
"conditions": [
{
"source": "header",
"key": "x-risk-level",
"operator": "=",
"value": "3"
}
],
"response": {
"products": [
{ "type": "high-risk", "yield": "8%" },
{ "type": "medium-risk", "yield": "5%" }
]
}
}
8. 常见问题排查
8.1 拦截不生效
- 检查环境:确认非生产环境
- 检查URL匹配:规则是否匹配请求URL
- 查看请求日志:面板中是否有请求记录
8.2 数据不符合预期
- 规则优先级:高优先级规则会覆盖低优先级
- 条件匹配:检查参数是否满足所有条件
- AI生成提示:检查字段描述是否清晰
8.3 性能问题
- 规则数量:单个接口建议不超过20条规则
- 匹配复杂度:避免使用大量正则表达式
- 数据大小:单个响应建议不超过1MB
9. 经验总结
在实际使用中,我们总结了以下最佳实践:
-
文档即Mock:
- 保持接口文档详细描述字段含义
- 使用标准枚举值而非自由文本
- AI生成的数据质量与文档质量正相关
-
渐进式Mock:
- 初期使用AI自动生成基础数据
- 中期补充边界用例
- 后期基于真实数据优化
-
团队协作:
- 建立Mock数据评审机制
- 核心业务场景由测试同学维护
- 定期清理过期规则
-
版本管理:
- 对团队共享规则进行版本控制
- 重大变更时创建新版本
- 保留历史版本供回滚
这套工具在落地过程中,最大的挑战不是技术实现,而是如何让团队成员改变原有的Mock使用习惯。我们通过内部培训、文档沉淀和定期复盘,逐步建立了规范的使用流程。现在,新成员入职第一天就能获得全套测试数据,再也不用为联调阻塞而烦恼了。
