企业级 Vue3 + Vite 项目实战中vite-plugin-mock 的最佳实践方案,解决开发中不依赖后端的痛点
在前端开发过程中经常会遇到后端数据缺失或后端服务尚未就绪的情况。此时我们可以通过mock数据来模拟真实接口确保开发工作不受影响。接下来介绍下企业级 Vue3 Vite 项目实战中vite-plugin-mock 的最佳实践方案。一、先说适用范围与局限vite-plugin-mock 原理在 Vite Dev ServerConnect 中间件层拦截请求只在vite dev生效不能用于 Vitest 单元测试或 Cypress E2E。✅ 适合前端独立开发、快速原型、无测试需求的中小型/中型项目⚠️ 不适合需要跨环境一致 mockVitest/Cypress、需要模拟网络超时/断网等极端场景建议用 MSW二、安装依赖pnpm add -D vite-plugin-mock mockjs types/mockjs # 如果要用生产环境演示 mock可选 pnpm add -D vite-plugin-mock/client三、推荐目录结构贴合你的企业级模板my-enterprise-app/ ├── mock/ # ✅ 根目录与 src 平级 │ ├── index.ts # 统一导出所有模块供插件读取 │ ├── types.ts # Mock 相关类型定义 │ ├── utils/ │ │ ├── helper.ts # 分页/延迟/响应包装 │ │ └── db.ts # 简易内存数据库CRUD 模拟 │ ├── fixtures/ │ │ └── users.ts # 固定基准数据 │ └── modules/ │ ├── user.ts # 用户模块接口 │ └── order.ts # 订单模块接口 ├── src/ │ ├── services/request/http.ts # Axios 实例 │ └── ... ├── vite.config.ts ├── .env.development └── .env.mock # 开启 mock 的环境文件四、vite.config.ts 配置关键// vite.config.ts import { defineConfig, loadEnv } from vite import vue from vitejs/plugin-vue import { viteMockServe } from vite-plugin-mock import path from node:path export default defineConfig(({ command, mode }) { const env loadEnv(mode, process.cwd()) // 通过环境变量控制是否启用 mock const enableMock env.VITE_USE_MOCK true command serve return { plugins: [ vue(), viteMockServe({ mockPath: mock/modules, // mock 模块目录 enable: enableMock, // 按环境开关 logger: true, // 控制台打印拦截日志 watchFiles: true, // 修改 mock 文件热更新 // supportTs: true — v3 默认支持可不写 }), ], resolve: { alias: { : path.resolve(__dirname, src), }, }, server: { port: 3000, // ⚠️ 重要proxy 不能抢走 mock 要拦截的路径 // 如果配了 proxy[/api]要确保 mock 先匹配 proxy: { // /api: { target: http://localhost:8080, changeOrigin: true } // 建议mock 开启时注释掉对应 /api proxy或只 proxy 非 mock 路径 }, }, } }).env.development联调后端VITE_USE_MOCKfalse VITE_API_BASE_URLhttp://localhost:8080.env.mock前端独立开发VITE_USE_MOCKtrue VITE_API_BASE_URL # 留空vite-plugin-mock 拦截相对路径 /api/xxx五、Axios 适配要点vite-plugin-mock 拦截的是相对路径请求所以 mock 模式下baseURL要为空或/// src/services/request/http.ts import axios from axios import { loadEnv } from vite const isMock import.meta.env.VITE_USE_MOCK true const http axios.create({ baseURL: isMock ? : import.meta.env.VITE_API_BASE_URL, timeout: 15000, }) // 请求/响应拦截器照常写token 注入、错误统一处理 http.interceptors.request.use((config) { const token localStorage.getItem(token) if (token) config.headers!.Authorization Bearer ${token} return config }) http.interceptors.response.use( (res) res.data, // 根据你的后端结构拆包 (err) Promise.reject(err) ) export default http⚠️常见坑mock 开着时 AxiosbaseURL还配成http://localhost:8080请求变成绝对路径Vite 中间件拦截不到mock 失效。六、Mock 核心代码类型安全 业务结构类型定义// mock/types.ts import type { MockMethod } from vite-plugin-mock export interface ApiRespT unknown { code: number data: T message: string } export type AppMockMethod MockMethod工具函数分页 响应包装// mock/utils/helper.ts import type { ApiResp } from ../types export function successT(data: T, message ok): ApiRespT { return { code: 200, data, message } } export function fail(message: string, code 400): ApiRespnull { return { code, data: null, message } } /** 简易内存分页 */ export function paginateT( list: T[], page 1, pageSize 20 ): { items: T[]; total: number } { const start (page - 1) * pageSize return { items: list.slice(start, start pageSize), total: list.length, } }内存数据库模拟 CRUD 状态变更// mock/utils/db.ts import Mock from mockjs import type { User } from /domain/user/types // 复用你领域层类型 export let userDb: User[] Mock.mock({ list|30: [ { id: guid, name: cname, phone: /1[3-9]\d{9}/, email: email, role: [admin, user, editor], status|1: [active, disabled], createdAt: datetime, updatedAt: datetime, }, ], }).list用户模块 Mock完整 CRUD 分页 错误模拟// mock/modules/user.ts import type { MockMethod } from vite-plugin-mock import { success, fail, paginate } from ../utils/helper import { userDb } from ../utils/db import type { User, CreateUserDto } from /domain/user/types export default [ // 列表 分页 关键字搜索 { url: /api/users, method: get, response: ({ query }: any) { const page Number(query.page) || 1 const pageSize Number(query.pageSize) || 20 const keyword (query.keyword as string) || let filtered userDb if (keyword) { filtered filtered.filter( (u) u.name.includes(keyword) || u.phone.includes(keyword) ) } const { items, total } paginate(filtered, page, pageSize) return success({ items, total, page, pageSize }) }, }, // 详情 { url: /api/users/:id, method: get, response: ({ query, req }: any) { // path-to-regexp 匹配到的 param 在 req.params const id req.params?.id || query.id const user userDb.find((u) u.id id) if (!user) return fail(用户不存在, 404) return success(user) }, }, // 新增 { url: /api/users, method: post, response: ({ body }: any) { const dto body as CreateUserDto if (!dto.phone) return fail(手机号不能为空) const newUser: User { id: crypto.randomUUID(), name: dto.name || 新用户, phone: dto.phone, email: dto.email || , role: dto.role || user, status: active, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), } userDb.unshift(newUser) return success(newUser, 创建成功) }, }, // 编辑 { url: /api/users/:id, method: put, response: ({ body, req }: any) { const id req.params?.id const idx userDb.findIndex((u) u.id id) if (idx -1) return fail(用户不存在, 404) userDb[idx] { ...userDb[idx], ...body, updatedAt: new Date().toISOString() } return success(userDb[idx], 更新成功) }, }, // 删除 { url: /api/users/:id, method: delete, response: ({ req }: any) { const id req.params?.id userDb userDb.filter((u) u.id ! id) return success(null, 删除成功) }, }, // 批量删除 { url: /api/users/batch-delete, method: post, response: ({ body }: any) { const { ids } body as { ids: string[] } userDb userDb.filter((u) !ids.includes(u.id)) return success(null, 批量删除成功) }, }, ] as MockMethod[]统一入口供 mockPath 自动扫描也可手动 import// mock/index.ts // 如果只是让插件扫描 modules/* 可留空导出 // 如需手动注册或有额外逻辑可在这里聚合 export {}七、package.json 启动脚本{ scripts: { dev: vite --mode development, dev:mock: vite --mode mock } }运行pnpm dev:mock→ 读取.env.mock→VITE_USE_MOCKtrue→ mock 启动运行pnpm dev→ 读取.env.development→ mock 关闭 → 请求走 proxy 到真实后端八、可选生产环境演示用 Mock慎用vite-plugin-mock 支持打包进生产构建仅用于演示/展示环境// mockProdServer.ts — 项目根目录 import { createProdMockServer } from vite-plugin-mock/client import userMock from ./mock/modules/user import orderMock from ./mock/modules/order export function setupProdMockServer() { createProdMockServer([...userMock, ...orderMock]) }// src/main.ts — 顶部注入 if (import.meta.env.PROD import.meta.env.VITE_USE_MOCK true) { import(../mockProdServer).then(({ setupProdMockServer }) { setupProdMockServer() }) }⚠️不建议常规生产开启会增加首屏体积且无法热更新仅用于内网演示服务器。九、常见坑位排查清单现象原因解决mock 不生效请求 404 / 直连后端AxiosbaseURL是绝对路径mock 模式baseURLmock 不生效Vite 日志无 Loaded mockmockPath配错或文件未export default []确认 TS 文件as MockMethod[]且 default 导出数组query 参数取不到handler 里要从{ query, req }解构req.params取路径参数query取?a1修改 mock 文件不热更新watchFiles: false或文件被 ignore确认watchFiles: true不被.gitignore忽略proxy 和 mock 冲突/api被 Vite proxy 先匹配mock 开启时注释掉对应 proxy或确保 mock url 先注册十、一句话总结vite-plugin-mock 最佳实践 按模块拆分 与 Axios baseURL 解耦 环境变量控制开关 复用 Domain 类型 Mock.js 生成数据 内存 DB 模拟 CRUD