Mock.js 数据生成与接口拦截
概述
Mock.js 是前端本地 Mock 方案的核心工具,通过数据模板语法生成结构化假数据,并拦截 XMLHttpRequest 请求返回预设响应。它无需启动任何后端服务,即可让前端在后端接口未就绪时独立完成页面开发与调试。
前置知识
- Mock 数据方案与工具选型
- JavaScript 对象与 JSON 数据结构
- XMLHttpRequest 基本工作原理
学习目标
- 掌握 Mock.js 数据模板(DTD)语法规范
- 理解 XHR 拦截机制与
Mock.mock()API - 能够在 Vite/Webpack 项目中集成 Mock.js
- 掌握白名单机制实现 Mock 与真实接口共存
一、数据模板语法(DTD)
Mock.js 的核心是数据模板定义(Data Template Definition),通过占位符和规则生成随机数据。
1.1 基本格式
code
属性名|规则: 值1.2 常用占位符
| 占位符 | 说明 | 示例输出 |
|---|---|---|
@cname | 中文姓名 | "张伟" |
@ctitle(min, max) | 中文标题 | "前端工程化实践" |
@email | 邮箱 | "test@example.com" |
@url | URL | "http://example.com" |
@date | 日期 | "2026-03-08" |
@datetime | 日期时间 | "2026-03-08 14:30:00" |
@integer(min, max) | 整数 | 42 |
@float(min, max, dmin, dmax) | 浮点数 | 3.14 |
@boolean | 布尔值 | true |
@id | 身份证 | "110101199001011234" |
@phone | 手机号 | "13800138000" |
@image(size) | 图片URL | 占位图地址 |
@paragraph(min, max) | 段落文本 | 随机英文段落 |
1.3 规则语法
javascript
const data = Mock.mock({
// 生成 3~8 条记录
'list|3-8': [
{
// id 自增,起始值 1
'id|+1': 1,
// 中文标题,5~12 字
'title': '@ctitle(5, 12)',
// 作者名
'author': '@cname',
// 阅读量 0~10000
'views|0-10000': 1,
// 是否置顶,概率 30%
'isTop|1-3': true,
// 标签,从数组中随机取 1~3 个
'tags|1-3': ['JavaScript', 'Vue', 'React', 'Node.js'],
// 创建时间
'createdAt': '@datetime("yyyy-MM-dd HH:mm:ss")'
}
],
// 分页信息
'total|10-100': 1,
'page': 1,
'pageSize': 10
})1.4 规则速查
| 规则格式 | 含义 |
|---|---|
name|min-max | 生成 min~max 个重复 |
name|count | 生成 count 个重复 |
name|+step | 自增,步长为 step |
name|min-max.dmin-dmax | 浮点数,整数部分 min |
name|1 | 布尔值,概率 50% |
name|1-3 | 布尔值,true 概率为 1/(1+3)=25%... 实际为 min/(min+max) |
二、XHR 拦截机制
2.1 工作原理
图表渲染中…
Mock.js 通过重写 XMLHttpRequest 原型方法,在请求发出前进行 URL 匹配:
- 命中规则 → 直接返回 Mock 数据,请求不会到达服务器
- 未命中 → 调用原始 XHR 方法,请求正常发出
2.2 Mock.mock() API
javascript
import Mock from 'mockjs'
// 拦截 GET /api/users 请求
Mock.mock('/api/users', 'get', {
'code': 0,
'message': 'success',
'data|5-10': [
{
'id|+1': 1,
'name': '@cname',
'email': '@email'
}
]
})
// 拦截带参数的 POST 请求
Mock.mock('/api/login', 'post', (options) => {
const { username, password } = JSON.parse(options.body)
if (username === 'admin' && password === '123456') {
return { code: 0, data: { token: '@guid' } }
}
return { code: 401, message: '用户名或密码错误' }
})
// 正则匹配 URL
Mock.mock(/\/api\/users\/\d+/, 'get', {
'code': 0,
'data': {
'id': '@id',
'name': '@cname',
'bio': '@paragraph(2, 4)'
}
})2.3 请求延迟模拟
javascript
// 设置全局延迟:200~600ms 随机
Mock.setup({ timeout: '200-600' })
// 针对单个规则设置延迟
Mock.mock('/api/slow', 'get', {
'code': 0,
'data': '@ctitle'
})三、项目集成方案
3.1 Vite 项目集成(推荐)
利用 Vite 的 import.meta.glob 实现 Mock 文件自动导入:
code
src/
├── mock/
│ ├── index.js # 统一入口
│ ├── modules/
│ │ ├── user.js # 用户模块
│ │ ├── course.js # 课程模块
│ │ └── order.js # 订单模块javascript
// src/mock/index.js
import Mock from 'mockjs'
// 自动导入 modules 目录下所有文件
const modules = import.meta.glob('./modules/*.js', { eager: true })
Object.values(modules).forEach((module) => {
const mocks = module.default || module
if (Array.isArray(mocks)) {
mocks.forEach(({ url, method, response }) => {
Mock.mock(url, method, response)
})
}
})
// 全局延迟
Mock.setup({ timeout: '200-500' })
export default Mockjavascript
// src/mock/modules/user.js
export default [
{
url: '/api/users',
method: 'get',
response: {
'code': 0,
'data|10-20': [
{ 'id|+1': 1, 'name': '@cname', 'role|1': ['admin', 'user'] }
]
}
},
{
url: '/api/users/login',
method: 'post',
response: (options) => {
const { username } = JSON.parse(options.body)
return { code: 0, data: { username, token: '@guid' } }
}
}
]3.2 条件加载(仅开发环境)
javascript
// src/main.js
async function bootstrap() {
if (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK === 'true') {
await import('./mock')
console.log('[Mock] Mock 数据已启用')
}
// 正常启动应用
createApp(App).mount('#app')
}
bootstrap()bash
# .env.development
VITE_USE_MOCK=true
# .env.production
VITE_USE_MOCK=false四、白名单机制
实际项目中,部分接口可能已由后端提供,需要让 Mock 仅拦截未就绪的接口:
javascript
// src/mock/index.js
const WHITE_LIST = [
'/api/config', // 已联调,走真实接口
'/api/upload', // 文件上传,走真实接口
]
// 重写 Mock.mock,增加白名单过滤
const originalMock = Mock.mock.bind(Mock)
Mock.mock = (url, method, response) => {
if (WHITE_LIST.some((path) => url.includes(path))) {
return // 白名单内不注册 Mock 规则
}
originalMock(url, method, response)
}五、Faker.js 替代方案
对于国际化项目或需要更丰富数据类型的场景,可考虑 Faker.js:
| 对比维度 | Mock.js | Faker.js |
|---|---|---|
| 数据语言 | 中文为主 | 多语言支持 |
| 拦截能力 | 内置 XHR 拦截 | 仅数据生成,需配合 MSW |
| 模板语法 | DTD 占位符 | 函数式 API |
| 维护状态 | 更新较少 | 社区活跃(@faker-js/faker) |
| 适用场景 | 国内项目快速 Mock | 国际化、复杂数据场景 |
javascript
// Faker.js 示例
import { faker } from '@faker-js/faker/locale/zh_CN'
const users = Array.from({ length: 10 }, () => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
avatar: faker.image.avatar(),
createdAt: faker.date.past()
}))常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Mock 拦截不生效 | 注册时机晚于请求发出 | 确保 Mock 注册在应用初始化之前 |
| fetch 请求未被拦截 | Mock.js 仅拦截 XHR | 使用 axios 或引入 fetch 拦截 polyfill |
| 生产环境泄露 Mock 数据 | 未做环境判断 | 通过环境变量 + 动态 import 隔离 |
| 数据每次刷新都变化 | 随机生成特性 | 需要固定数据时使用 JSON Server |
最佳实践
- 模块化组织:按业务模块拆分 Mock 文件,利用 glob 自动导入
- 环境隔离:Mock 代码仅在开发环境加载,生产构建时 tree-shaking 移除
- 接口契约对齐:Mock 数据结构严格参照接口文档,避免联调时字段不匹配
- 延迟模拟:设置合理延迟(200-600ms),暴露 loading 状态和竞态问题
- 白名单渐进切换:随后端接口就绪,逐步将接口加入白名单
延伸阅读
- 上一篇:Mock 数据方案与工具选型
- 下一篇:JSON Server 实战指南