{T}

Mock.js 数据生成与接口拦截

概述

Mock.js 是前端本地 Mock 方案的核心工具,通过数据模板语法生成结构化假数据,并拦截 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"
@urlURL"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浮点数,整数部分 minmax,小数部分 dmindmax 位
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 Mock
javascript
// 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.jsFaker.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

最佳实践

  1. 模块化组织:按业务模块拆分 Mock 文件,利用 glob 自动导入
  2. 环境隔离:Mock 代码仅在开发环境加载,生产构建时 tree-shaking 移除
  3. 接口契约对齐:Mock 数据结构严格参照接口文档,避免联调时字段不匹配
  4. 延迟模拟:设置合理延迟(200-600ms),暴露 loading 状态和竞态问题
  5. 白名单渐进切换:随后端接口就绪,逐步将接口加入白名单

延伸阅读