我的项目实战(四) - 数据请求接口,Mockjs的创建使用

0 阅读8分钟

在现代前端工程化开发中,前后端分离已成为标准模式。我们不再依赖后端“先写完接口”,而是基于约定的 API 文档,各自并行推进。

但问题来了:

后端还没给接口,我怎么写列表页?没有数据,页面就是一片空白。

这时候,一个看似简单却极为关键的技术浮出水面——接口数据模拟(Mock)

今天继续推进我们的实战项目,带你一步步理解:
如何使用 Mock.js 搭建本地假数据服务,支撑前端独立开发,并深入剖析其背后的设计思想、实现细节与注意事项。


一、为什么需要 Mock?不是联调才开始吗?

很多刚入行的同学会误以为:“前端做完样式就等后端。”
但实际上,在成熟的团队协作中,前端不应该等待后端

举个例子:

你要开发一个「文章列表页」,需求如下:

  • 显示标题、摘要、作者头像、发布时间;
  • 展示标签(如“前端”、“算法”);
  • 支持分页加载,每页10条;
  • 点击进入详情页。

此时,如果等到后端把 /api/posts?page=1&limit=10 接口写好再开工,你的进度就被锁死了。

而如果你能提前定义好这个接口的响应格式,自己造一批结构一致的数据,就可以立刻开始:

  • 调试组件渲染;
  • 验证分页逻辑;
  • 测试错误边界处理;
  • 甚至完成初步性能优化。

这正是 Mock 的核心价值:让前端摆脱对后端的实时依赖,提升开发自主性与效率


二、Mock.js 是什么?它解决了哪些问题?

Mock.js 是一个用于生成随机数据、拦截 Ajax 请求的前端库。它的最大特点是:

  • 使用简洁语法生成符合规则的假数据;
  • 可模拟复杂嵌套结构(如用户信息、图片数组);
  • 支持动态函数逻辑(比如随机选标签);
  • 结合构建工具可在开发环境自动启用,上线前关闭。

但它本身并不直接提供 HTTP 服务。我们需要借助像 vite-plugin-mock 这样的插件,将 Mock 数据挂载到指定路由上,使其表现得和真实接口一模一样。


三、实战:搭建一个可分页的文章列表 Mock 服务

我们以一个常见的内容平台为背景,来实现 /api/posts 接口的 Mock。

第一步:确定接口契约(API Contract)

在动手之前,必须和后端达成一致。哪怕只是口头约定,也要明确:

GET /api/posts?page=1&limit=10

响应:
{
  "code": 200,
  "msg": "success",
  "items": [...],
  "pagination": {
    "total": 45,
    "current": 1,
    "limit": 10,
    "totalPages": 5
  }
}

这就是所谓的“接口契约”。只要双方遵守这个结构,后续切换真实接口时就能无缝对接。


第二步:生成符合业务特征的假数据

我们现在要用 Mock.js 构造 45 条具备真实感的文章数据。

1. 定义基础字段

{
  title: '@ctitle(8,20)',          // 中文标题,8~20字
  brief: '@ctitle(20,100)',         // 摘要作为短文本
  totalComments: '@integer(1,30)',  // 评论数在合理范围
  totalLikes: '@integer(0,300)',    // 点赞数有差异
  publishedAt: '@datetime("yyyy-MM-dd HH:mm")', // 时间格式统一
  id: '@increment(1)'               // 自增 ID,便于调试
}

这些 @ 开头的写法是 Mock.js 的占位符语法,非常直观。相比手动 new Array 填充对象,效率高出许多。

2. 复杂结构处理:用户信息 + 图片资源

文章通常关联作者:

user: {
  id: '@integer(1,1000)',
  name: '@cname()',
  avatar: '@image(300x200)'
}

这里 @cname() 生成中文姓名,@image(300x200) 返回一个 base64 编码的占位图 URL,可以直接用于 <img src>,无需额外准备图片资源。

再比如封面图和图集:

thumbnail: '@image(300x200)',
pics: [
  '@image(300x200)',
  '@image(300x200)',
  '@image(300x200)'
]

虽然都是静态图,但视觉上足够支撑 UI 开发。

3. 特殊逻辑:标签随机选取

标签不能全一样,否则测试不到多态性。我们可以这样设计:

const tags = ["前端", "后端", "AI", "职场", "副业", "面经", "算法"];

tags: () => Mock.Random.pick(tags, 2)

注意这里是函数形式,确保每次生成都独立选择两个不同标签。如果是直接写 tags: ['前端','算法'],那所有数据都会一样,失去模拟意义。


第三步:支持分页查询能力

光有数据还不够,接口还要能处理参数。

我们需要解析请求中的 pagelimit,返回对应的子集。

response: ({ query }) => {
  const { page = '1', limit = '10' } = query;
  const currentPage = parseInt(page, 10);
  const size = parseInt(limit, 10);

  // 参数校验
  if (isNaN(currentPage) || isNaN(size) || currentPage <= 0 || size <= 0) {
    return { code: 400, msg: 'Invalid page or pageSize' };
  }

  const total = posts.length;
  const start = (currentPage - 1) * size;
  const end = start + size;
  const paginatedData = posts.slice(start, end);

  return {
    code: 200,
    msg: 'success',
    items: paginatedData,
    pagination: {
      total,
      current: currentPage,
      limit: size,
      totalPages: Math.ceil(total / size)
    }
  };
}

这段代码的关键点在于:

  • 类型安全转换:URL 参数永远是字符串,必须 parseInt 并做合法性判断;
  • 边界控制:避免负数或零导致数组越界;
  • 分页计算清晰:起始索引 = (当前页 - 1) * 每页数量
  • 返回完整元信息:总数、总页数等帮助前端控制分页器状态。

这样一来,无论你访问 /api/posts?page=2&limit=10 还是 page=3&limit=5,都能拿到正确的结果。


四、如何接入项目?Vite 下的集成方案

有了 Mock 数据,还需要让它跑在正确的路径上。

我们使用 vite-plugin-mock 插件来激活 Mock 功能。

1. 安装依赖

pnpm add mockjs -D
pnpm add vite-plugin-mock -D

2. 配置 vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteMockServe } from 'vite-plugin-mock';

export default defineConfig({
  plugins: [
    react(),
    viteMockServe({
      mockPath: 'mock',     // 存放 mock 文件的目录
      localEnabled: true,    // 开发环境启用
      prodEnabled: false,    // 生产环境禁用
      injectCode: `
        import { setupProdMockServer } from '../mock/_createProductionServer';
        setupProdMockServer();
      `,
    })
  ]
});

⚠️ 注意:生产环境默认不开启 Mock。若需在打包后仍使用(如演示环境),可通过 prodEnabled 控制,并注入启动代码。

3. 目录结构组织

/src
├── mock/
│   └── posts.js
├── api/
│   └── posts.ts
└── types/
    └── index.ts

只要文件导出的是数组或对象,vite-plugin-mock 就会自动注册其中的路由规则。


五、前端调用方式:像请求真实接口一样自然

我们在 api/posts.ts 中封装请求方法:

import axios from './config';
import type { Post } from '@/types';

interface PostsResponse {
  items: Post[];
  pagination: {
    total: number;
    current: number;
    limit: number;
    totalPages: number;
  };
}

export const fetchPosts = async (
  page: number = 1,
  limit: number = 10
): Promise<PostsResponse> => {
  try {
    const res = await axios.get('/posts', {
      params: { page, limit }
    });
    return res.data;
  } catch (err) {
    console.error('[fetchPosts] 请求失败', err);
    return { items: [], pagination: { total: 0, current: page, limit, totalPages: 0 } };
  }
};

你会发现,无论是调用 Mock 还是真实接口,这段代码完全不需要修改

唯一的区别只是:

  • 开发时:axios 请求被拦截,返回 Mock 数据;
  • 上线后:指向真实域名,获取真实响应。

只要接口结构不变,替换零成本。


六、优点总结:为什么推荐使用这种方式?

优势说明
✅ 提升开发效率前端可独立工作,不卡在联调阶段
✅ 降低沟通成本所有人都清楚接口长什么样
✅ 更早发现问题可模拟异常情况(如空数据、超长标题)
✅ 支持多种场景分页、筛选、排序均可提前验证
✅ 易于维护数据结构集中管理,修改方便

更重要的是,这种做法推动团队形成良好的协作习惯——先定接口,再写代码


七、需要注意的问题与最佳实践

尽管 Mock 很强大,但也有一些陷阱需要注意:

❌ 不要过度拟真

有些人喜欢用 Mock 生成几千条数据测试滚动性能。这其实没必要。Mock 应服务于功能开发,而不是性能压测。真正性能问题要在真实数据环境下暴露。

❌ 忽视类型一致性

务必保证 Mock 返回的数据结构和 TypeScript 类型定义严格匹配。例如:

type Post = {
  title: string;
  totalLikes: number;
  tags: string[];
}

如果你在 Mock 中让 totalLikes 返回字符串,编译不会报错,但运行时报错,这就失去了类型保护的意义。

✅ 建议:把 Mock 数据抽象成 Schema

可以单独抽离一份 post.mock.schema.ts

export const postSchema = {
  title: '@ctitle(8,20)',
  totalLikes: '@integer(0,300)',
  tags: () => Mock.Random.pick(tagList, 2),
  // ...
};

然后在多个地方复用,提高可维护性。

✅ 建议:保留切换开关

可以在项目中加一个全局变量或配置项,允许临时关闭 Mock,直连后端测试:

// .env.development
VITE_USE_MOCK=true

通过环境变量控制是否启用 Mock 插件,方便快速对比。


八、结语:善用 Mock“前置验证”,独立闭环

很多人误解 Mock 是“骗自己”。其实恰恰相反:

Mock 是一种主动设计行为,是对接口契约的具象化表达。

当你能准确描述出“我需要什么样的数据”,说明你已经理解了业务逻辑。而当你能模拟出分页、错误、加载等各种状态时,你的代码健壮性自然提升。

拿起 Mock.js,从今天起做一个能独立闭环交付的前端开发者。


附:学习建议路线

  1. 先读接口文档,画出预期响应结构;
  2. 用 Mock.js 写出对应数据模板;
  3. 在前端调用并渲染;
  4. 等后端完成后,仅修改 baseURL 即可切换;
  5. 对比差异,完善边界处理。

这才是真正的“前后端分离”开发之道。