mock.js在Vue3+TypeScript中的使用指南

16 阅读4分钟

一、安装

npm install mockjs --save-dev
npm install @types/mockjs --save-dev

二、基本配置

1.创建Mock入口文件(src/mock/index.ts
import Mock from 'mockjs'
// 配置 Mock
Mock.setup({
  timeout: '200-600', // 模拟网络延迟,随机 200-600ms
})

// 引入各个模块的 mock
import './menu'
import './auth'
// import './user'
// import './order'

console.log('[Mock] Mock 服务已启动')
2.在main.ts中引入(仅开发环境)
//src/main.ts
if (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK === 'true') {
  import('./mock/index')
}

三、创建Mock数据的标准写法

1.使用Mock.js语法生成随机数据
// src/mock/user.ts
import Mock from 'mockjs'
import type { UserResponse, UserListParams } from '@/api/types'

// 使用 Mock.js 生成随机数据
export const userMockData = Mock.mock({
  'list|10-20': [
    {
      'id|+1': 1,
      'name': '@cname', // 随机中文姓名
      'email': '@email', // 随机邮箱
      'phone': /^1[3-9]\d{9}$/, // 正则表达式生成手机号
      'age|18-60': 1, // 18-60 之间的随机数
      'avatar': '@image("200x200", "@color", "@name")', // 随机图片
      'address': '@county(true)', // 随机地址
      'createTime': '@datetime', // 随机日期时间
      'status|1': ['active', 'inactive'], // 随机选择
    }
  ],
  'total': 100,
})

// Mock 接口
Mock.mock(/\/api\/user\/list/, 'get', (options: any): UserResponse => {
  const url = options?.url || ''
  const params = new URLSearchParams(url.split('?')[1] || '')
  const page = Number(params.get('page')) || 1
  const pageSize = Number(params.get('pageSize')) || 10
  
  return {
    code: 200,
    message: '获取用户列表成功',
    data: {
      list: userMockData.list.slice((page - 1) * pageSize, page * pageSize),
      total: userMockData.total,
      page,
      pageSize,
    },
  }
})
2.使用静态数据(推荐用于固定数据)
// src/mock/menu.ts
import Mock from 'mockjs'
import type { MenuItem, MenuResponse } from '@/api/types'

// 静态菜单数据
export const menuMockData: MenuItem[] = [
  {
    id: 'dashboard',
    title: '仪表盘',
    path: '/dashboard',
    icon: 'TrendCharts',
    meta: {
      title: '仪表盘',
      icon: 'TrendCharts',
    },
  },
  // ... 更多菜单项
]

// Mock 接口
Mock.mock(/\/api\/menu\/list/, 'get', (options: any): MenuResponse => {
  return {
    code: 200,
    message: '获取菜单成功',
    data: menuMockData,
  }
})

四、常用Mock.js语法

1.基础数据类型
Mock.mock({
  // 字符串
  'name': '@cname',        // 中文姓名
  'nameEn': '@name',      // 英文姓名
  'word': '@word',        // 单词
  'sentence': '@sentence', // 句子
  'paragraph': '@paragraph', // 段落
  
  // 数字
  'age|18-60': 1,         // 18-60 之间的随机数
  'score|1-100': 1,       // 1-100 之间的随机数
  'price|10.2-100.2': 1,  // 10.2-100.2 之间的随机小数(保留2位)
  
  // 布尔值
  'isActive|1': true,     // 50% 概率为 true
  'isVip|1-2': true,      // 1/3 概率为 true
  
  // 日期
  'date': '@date',        // 日期:2023-01-01
  'datetime': '@datetime', // 日期时间:2023-01-01 12:00:00
  'time': '@time',        // 时间:12:00:00
  'now': '@now',          // 当前时间
  
  // 图片
  'avatar': '@image("200x200")', // 200x200 的随机图片
  'image': '@image("300x200", "#50B347", "#FFF", "Mock")',
  
  // 颜色
  'color': '@color',      // 随机颜色
  'hex': '@hex',          // 十六进制颜色
  
  // 其他
  'url': '@url',          // 随机 URL
  'email': '@email',      // 随机邮箱
  'ip': '@ip',            // 随机 IP
  'id': '@id',            // 随机 ID
  'guid': '@guid',        // GUID
})
2.数组生成
Mock.mock({
  // 生成 1-10 个元素的数组
  'list|1-10': [
    {
      'id|+1': 1,  // 自增,从 1 开始
      'name': '@cname',
    }
  ],
  
  // 生成固定数量的数组
  'items|5': [
    {
      'id|+1': 1,
      'name': '@word',
    }
  ],
})

五.完整的Mock文件

// src/mock/order.ts
import Mock from 'mockjs'
import type { OrderResponse, OrderListParams } from '@/api/types'

// 订单状态枚举
const orderStatus = ['pending', 'paid', 'shipped', 'completed', 'cancelled']

// 生成订单列表数据
const generateOrderList = (count: number = 10) => {
  return Mock.mock({
    [`list|${count}`]: [
      {
        'id|+1': 1000,
        'orderNo': '@id', // 随机订单号
        'userId|1-100': 1,
        'userName': '@cname',
        'amount|100-10000.2': 1, // 100-10000 之间的金额,保留2位小数
        'status|1': orderStatus,
        'createTime': '@datetime',
        'payTime': '@datetime',
        'items|1-5': [
          {
            'productId|+1': 1,
            'productName': '@ctitle(5,10)', // 5-10 个字的标题
            'quantity|1-10': 1,
            'price|10-1000.2': 1,
          }
        ],
      }
    ],
  }).list
}

// GET 请求:获取订单列表
Mock.mock(/\/api\/order\/list/, 'get', (options: any): OrderResponse => {
  const url = options?.url || ''
  const params = new URLSearchParams(url.split('?')[1] || '')
  
  const page = Number(params.get('page')) || 1
  const pageSize = Number(params.get('pageSize')) || 10
  const status = params.get('status') || ''
  
  // 生成数据
  let list = generateOrderList(50) // 生成 50 条数据
  
  // 根据状态过滤
  if (status) {
    list = list.filter((item: any) => item.status === status)
  }
  
  // 分页
  const start = (page - 1) * pageSize
  const end = start + pageSize
  const pageList = list.slice(start, end)
  
  return {
    code: 200,
    message: '获取订单列表成功',
    data: {
      list: pageList,
      total: list.length,
      page,
      pageSize,
    },
  }
})

// POST 请求:创建订单
Mock.mock(/\/api\/order\/create/, 'post', (options: any) => {
  const body = typeof options.body === 'string' 
    ? JSON.parse(options.body) 
    : options.body
  
  const newOrder = Mock.mock({
    id: '@id',
    orderNo: '@id',
    userId: body.userId,
    amount: body.amount,
    status: 'pending',
    createTime: '@now',
    items: body.items,
  })
  
  return {
    code: 200,
    message: '创建订单成功',
    data: newOrder,
  }
})

// GET 请求:获取订单详情
Mock.mock(/\/api\/order\/detail/, 'get', (options: any) => {
  const url = options?.url || ''
  const orderId = url.split('/').pop() || ''
  
  const order = Mock.mock({
    id: orderId,
    orderNo: '@id',
    userId: '@id',
    userName: '@cname',
    amount: '@float(100, 10000, 2, 2)',
    status: 'paid',
    createTime: '@datetime',
    payTime: '@datetime',
    address: '@county(true)',
    items: [
      {
        productId: 1,
        productName: '@ctitle',
        quantity: 2,
        price: 99.99,
      }
    ],
  })
  
  return {
    code: 200,
    message: '获取订单详情成功',
    data: order,
  }
})

六、类型安全的Mock写法

// src/mock/types.ts
export interface MockResponse<T = any> {
  code: number
  message: string
  data: T
}

// src/mock/user.ts
import Mock from 'mockjs'
import type { User, UserListResponse } from '@/api/types'
import type { MockResponse } from './types'

// 类型安全的 Mock 函数
const createMockResponse = <T>(
  data: T,
  message: string = '操作成功'
): MockResponse<T> => {
  return {
    code: 200,
    message,
    data,
  }
}

// 使用
Mock.mock(/\/api\/user\/list/, 'get', (): MockResponse<UserListResponse> => {
  const list = generateUserList(20)
  return createMockResponse({
    list,
    total: list.length,
  }, '获取用户列表成功')
})

七、最佳实践

1.统一响应式格式
// src/mock/utils.ts
export const createSuccessResponse = <T>(
  data: T,
  message: string = '操作成功'
) => ({
  code: 200,
  message,
  data,
})

export const createErrorResponse = (
  message: string = '操作失败',
  code: number = 500
) => ({
  code,
  message,
  data: null,
})
2.统一使用
import { createSuccessResponse, createErrorResponse } from './utils'

Mock.mock(/\/api\/user\/list/, 'get', () => {
  return createSuccessResponse(userList, '获取用户列表成功')
})

Mock.mock(/\/api\/user\/create/, 'post', (options) => {
  // 验证逻辑
  if (!options.body) {
    return createErrorResponse('参数错误', 400)
  }
  return createSuccessResponse(newUser, '创建用户成功')
})
3.环境控制
// src/mock/index.ts
if (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK === 'true') {
  // 只在开发环境且启用 mock 时加载
  import('./menu')
  import('./auth')
}

当你决定不再使用Mock数据,而是直接调用真实的后端接口提供数据,核心思想就是:移除或禁用Mock,直接用fetch/axios调真实API。

1.停止使用Mock.js

import './mock' // ← 删除或注释掉这行,这样的话所有请求都会走真实网络。

2.条件引入(推荐保留单仅开发使用) 后续还想mock做其他功能,可以加环境判断。

// main.js 
if (import.meta.env.DEV) {
// 仅在开发环境加载 mock(可选) 
// import('./mock')
}

如果确定要用真实接口,就不要引入mock数据。

3.在组件中直接调用真实接口

// ❌ 旧方式(依赖 mock 拦截 /api/chart) 
const res = await fetch('/api/chart-data')

有真实的后端接口比如

https://your-backend.com/api/v1/sales-report

然后vue3.0组件中 使用fetxh(原生无需安装)

<script setup>
import { onMounted, ref } from 'vue'
import * as echarts from 'echarts' 
const chartRef = ref(null) 
const loading = ref(true) 
onMounted(async () => { try { // ✅ 调用真实接口 
const response = await fetch('https://your-backend.com/api/v1/sales-report') if (!response.ok) throw new Error('请求失败')
const data = await response.json()
// 初始化 ECharts 
const chart = echarts.init(chartRef.value) 
chart.setOption({
title: { text: '销售报表' }, 
xAxis: { data: data.categories },
yAxis: {}, 
series: [{ type: 'bar', data: data.values }]
}) 
} catch (error) {
console.error('获取图表数据失败:', error)
} finally { 
loading.value = false 
 } 
}) 
</script> 
<template>
<div v-if="loading">加载中...</div>
<div ref="chartRef" style="width: 600px; height: 400px;"></div> 
</template>

或者使用axios(更加强大,支持拦截器,自动JSON)

1.安装

npm install axios
import axios from 'axios'
const { data } = await axios.get('https://your-backend.com/api/v1/sales-report') // 直接使用 data
处理跨域问题(如果前后端域名不用)

如果你的前端是 http://localhost:5173,而后端是 https://api.yourcompany.com,浏览器会因 CORS(跨域) 拦截请求。

解决方案

后端设置CORS头 后端需返回:

Access-Control-Allow-Origin: http://localhost:5173 // 或 *

开发阶段用Vite代理(临时方案) vite.config.js配置代理,避免跨域:

export default defineConfig({
server: { 
proxy: { 
    '/api': {
    target: 'https://your-backend.com',
    changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '/api/v1') 
    } 
   }
  } 
})

然后前端请求写成

fetch('/api/sales-report') // 实际转发到 https://your-backend.com/api/v1/sales-report

四、移除mock.js 如果确定不再用Mock,直接删除mock/目录和相关文件。让项目更干净。