以下是一个基于 Mock.js 的前端项目实战案例,以 “用户管理系统” 为例,展示如何在实际开发中使用 Mock.js 模拟后端接口,实现独立开发与测试。
项目背景
假设我们需要开发一个用户管理系统,包含用户列表查询、用户详情查看、新增用户、删除用户四个核心功能。后端接口尚未开发完成,前端需基于接口文档用 Mock.js 模拟数据,先行开发页面与交互。
技术栈
- 前端框架:Vue 3(或 React,思路一致)
- 请求库:Axios
- 模拟工具:Mock.js
- UI 组件库:Element Plus(可选)
实战步骤
步骤 1:环境搭建
-
创建 Vue 项目并安装依赖:
# 创建项目 vue create mock-demo cd mock-demo # 安装依赖 npm install axios mockjs --save npm install element-plus --save # 可选,用于快速搭建 UI -
目录结构规划:
src/ ├── api/ # 接口请求封装 │ └── user.js # 用户相关接口 ├── mock/ # Mock 配置 │ └── userMock.js # 用户接口模拟规则 ├── views/ # 页面组件 │ └── UserManage.vue # 用户管理页面 ├── main.js # 入口文件(引入 Mock)
步骤 2:定义接口文档(模拟)
假设后端提供的接口文档如下:
| 接口功能 | 请求地址 | 方法 | 请求参数 | 响应格式 |
|---|---|---|---|---|
| 获取用户列表 | /api/users | GET | page(页码)、size(每页条数) | { code: 200, data: { list: [], total: 100 } } |
| 获取用户详情 | /api/users/:id | GET | id(用户 ID) | { code: 200, data: { id, name, ... } } |
| 新增用户 | /api/users | POST | name、age、email | { code: 200, data: { id, ... } } |
| 删除用户 | /api/users/:id | DELETE | id(用户 ID) | { code: 200, message: "删除成功" } |
步骤 3:编写 Mock 规则(src/mock/userMock.js)
根据接口文档,用 Mock.js 定义模拟数据和请求拦截规则:
import Mock from 'mockjs';
import { param2Obj } from '../utils'; // 工具函数:解析 URL 参数(见下文)
// 模拟用户数据(全局变量,用于模拟“数据持久化”)
let userList = Mock.mock({
'list|100': [{ // 初始生成 100 条用户数据
'id|+1': 1, // 自增 ID
'name': '@cname', // 中文姓名
'age|18-60': 1, // 年龄 18-60
'email': '@email', // 随机邮箱
'gender|1': ['男', '女'], // 性别
'phone': '@phone', // 手机号
'address': '@city(true)', // 地址(省+市)
'createTime': '@datetime("yyyy-MM-dd HH:mm:ss")' // 创建时间
}]
}).list;
// 1. 拦截:获取用户列表(分页)
Mock.mock(/\/api\/users/, 'get', (options) => {
// 解析请求参数(page、size)
const { page = 1, size = 10 } = param2Obj(options.url);
const startIndex = (page - 1) * size;
const endIndex = startIndex + parseInt(size);
// 模拟分页逻辑
const mockList = userList.filter((_, index) => index >= startIndex && index < endIndex);
return {
code: 200,
data: {
list: mockList,
total: userList.length // 总条数
}
};
});
// 2. 拦截:获取用户详情
Mock.mock(/\/api\/users\/(\d+)/, 'get', (options) => {
// 从 URL 中提取 ID(如 /api/users/123 → 123)
const id = parseInt(options.url.match(/\/api\/users\/(\d+)/)[1]);
const user = userList.find(u => u.id === id);
return user
? { code: 200, data: user }
: { code: 404, message: '用户不存在' };
});
// 3. 拦截:新增用户
Mock.mock(/\/api\/users/, 'post', (options) => {
// 解析请求体(新增用户的信息)
const newUser = JSON.parse(options.body);
// 生成新 ID(最大 ID + 1)
const maxId = Math.max(...userList.map(u => u.id), 0);
newUser.id = maxId + 1;
newUser.createTime = Mock.mock('@datetime("yyyy-MM-dd HH:mm:ss")'); // 补充创建时间
// 添加到用户列表
userList.unshift(newUser); // 新增用户放最前面
return { code: 200, data: newUser, message: '新增成功' };
});
// 4. 拦截:删除用户
Mock.mock(/\/api\/users\/(\d+)/, 'delete', (options) => {
const id = parseInt(options.url.match(/\/api\/users\/(\d+)/)[1]);
const index = userList.findIndex(u => u.id === id);
if (index !== -1) {
userList.splice(index, 1); // 从列表中删除
return { code: 200, message: '删除成功' };
} else {
return { code: 404, message: '用户不存在' };
}
});
// 工具函数:解析 URL 参数为对象(如 ?page=1&size=10 → { page: '1', size: '10' })
export function param2Obj(url) {
const search = url.split('?')[1];
if (!search) return {};
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
);
}
步骤 4:封装 API 请求(src/api/user.js)
用 Axios 封装接口请求,与 Mock 拦截的地址对应:
import axios from 'axios';
// 1. 获取用户列表(分页)
export const getUserList = (page, size) => {
return axios.get('/api/users', { params: { page, size } });
};
// 2. 获取用户详情
export const getUserDetail = (id) => {
return axios.get(`/api/users/${id}`);
};
// 3. 新增用户
export const addUser = (userData) => {
return axios.post('/api/users', userData);
};
// 4. 删除用户
export const deleteUser = (id) => {
return axios.delete(`/api/users/${id}`);
};
步骤 5:在页面中使用(src/views/UserManage.vue)
开发用户管理页面,调用 API 接口(实际会被 Mock 拦截):
<template>
<div class="user-manage">
<el-card>
<div class="header">
<h2>用户管理</h2>
<el-button type="primary" @click="showAddDialog = true">新增用户</el-button>
</div>
<!-- 用户列表 -->
<el-table :data="userList" border>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="姓名"></el-table-column>
<el-table-column prop="age" label="年龄"></el-table-column>
<el-table-column prop="gender" label="性别"></el-table-column>
<el-table-column prop="email" label="邮箱"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="handleDetail(scope.row.id)">查看</el-button>
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
:current-page="page"
:page-size="size"
:total="total"
@current-change="handlePageChange"
></el-pagination>
<!-- 新增用户弹窗 -->
<el-dialog v-model="showAddDialog" title="新增用户">
<el-form :model="newUser" label-width="80px">
<el-form-item label="姓名">
<el-input v-model="newUser.name"></el-input>
</el-form-item>
<el-form-item label="年龄">
<el-input v-model.number="newUser.age"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="newUser.email"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="newUser.gender">
<el-radio label="男"></el-radio>
<el-radio label="女"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showAddDialog = false">取消</el-button>
<el-button type="primary" @click="handleAdd">确定</el-button>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { getUserList, addUser, deleteUser } from '../api/user';
import { ElMessage } from 'element-plus';
// 分页参数
const page = ref(1);
const size = ref(10);
const total = ref(0);
const userList = ref([]);
// 新增用户弹窗
const showAddDialog = ref(false);
const newUser = ref({ name: '', age: 18, email: '', gender: '男' });
// 初始化加载用户列表
const loadUserList = async () => {
try {
const res = await getUserList(page.value, size.value);
userList.value = res.data.data.list;
total.value = res.data.data.total;
} catch (err) {
console.error('加载用户失败', err);
}
};
// 分页切换
const handlePageChange = (newPage) => {
page.value = newPage;
loadUserList();
};
// 新增用户
const handleAdd = async () => {
try {
await addUser(newUser.value);
ElMessage.success('新增成功');
showAddDialog.value = false;
newUser.value = { name: '', age: 18, email: '', gender: '男' }; // 重置表单
loadUserList(); // 刷新列表
} catch (err) {
ElMessage.error('新增失败');
}
};
// 删除用户
const handleDelete = async (id) => {
try {
await deleteUser(id);
ElMessage.success('删除成功');
loadUserList(); // 刷新列表
} catch (err) {
ElMessage.error('删除失败');
}
};
// 查看详情(简化处理)
const handleDetail = (id) => {
ElMessage.info(`查看 ID 为 ${id} 的用户详情`);
};
// 页面挂载时加载数据
onMounted(loadUserList);
</script>
<style scoped>
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
</style>
步骤 6:在入口文件中引入 Mock(src/main.js)
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
// 引入 Mock 配置(生产环境可注释掉)
import './mock/userMock';
createApp(App)
.use(router)
.use(ElementPlus)
.mount('#app');
关键实现说明
-
数据持久化模拟:用全局变量
userList存储用户数据,新增 / 删除操作直接修改该变量,模拟后端数据库的 “持久化” 效果,确保页面刷新后数据状态一致(实际开发中可结合 localStorage 进一步优化)。 -
动态参数处理:通过正则匹配 URL(如
/api/users/:id)和解析请求参数(page、size),模拟真实接口的动态路由和分页逻辑。 -
业务逻辑模拟:模拟了 “用户不存在”“删除成功 / 失败” 等场景,使前端能完整测试各种交互流程。
-
环境隔离:Mock 配置仅在开发环境引入,生产环境可通过注释或环境变量控制,避免影响真实接口。
运行效果
启动项目后,即可在页面上:
- 分页查看模拟的用户列表;
- 点击 “新增用户” 提交表单,新增数据会实时显示在列表;
- 点击 “删除” 按钮移除用户,列表自动刷新;
- 所有操作均无需后端接口,完全由 Mock.js 模拟响应。
总结
通过 Mock.js,前端可以在后端接口就绪前独立完成页面开发、交互逻辑和边界场景测试,大幅提升前后端并行开发效率。实际项目中,可根据接口文档扩展更多 Mock 规则(如异常状态码、复杂嵌套数据等),进一步贴近真实业务场景。
1901

被折叠的 条评论
为什么被折叠?



