「九九八十一难」第一难:前端数据mock指南(TS + VUE)

Vue3 + TypeScript 项目中使用 Mock 数据指南

背景

产品:这里有个需求,计划月底上线,你们评估下开发时间,保证月底能上线现网。

测试:我需要一周的测试时间,包括功能测试、性能测试、兼容性测试等。

UI:我需要一周时间,包括页面布局、交互设计、颜色方案等。

后端:我需要两周时间,包括数据库设计、接口开发、业务逻辑实现等。

前端:我走?

产品:你想想办法。

前端:我可以牺牲自己的开发时间,通过接口mock来并行开发,需要后端提前提供接口文档,我同步进行页面开发和逻辑实现。

为了需求正常上线,无私的前端又为自己找了个加班的机会。

前言

在前端开发过程中,我们经常会遇到后端接口尚未完成,但前端需要提前开发页面和功能的情况。 这时,使用 Mock 数据就成为了一种非常有效的解决方案。 本文将介绍如何在 Vue3 + TypeScript 项目中搭建和使用 Mock 数据。

什么是 Mock 数据

概念

Mock 数据是指在开发过程中,为了模拟后端接口返回的数据,而创建的虚假数据。

作用

  1. 并行开发:前端可以与后端同时开发,不需要等待后端接口完成

  2. 独立测试:可以模拟各种边界情况和错误场景

  3. 性能测试:可以模拟大量数据,测试前端性能

  4. 演示效果:在没有后端服务的情况下,也能展示完整的功能

优势

  1. 提高开发效率:减少等待后端接口的时间

  2. 增强代码健壮性:可以测试各种异常情况

  3. 改善团队协作:明确接口规范,减少沟通成本

  4. 简化测试流程:可以快速模拟各种场景

环境搭建

安装依赖

在 Vue3 + TypeScript 项目中,需要安装以下依赖:

bash 复制代码
# 安装 mockjs 库
npm install mockjs --save-dev

# 安装 vite-plugin-mock 插件
npm install vite-plugin-mock --save-dev

# 安装 @types/mockjs 类型定义(可选但推荐)
npm install --save-dev @types/mockjs

配置 Vite

vite.config.ts 文件中配置 vite-plugin-mock 插件:

typescript 复制代码
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
import { viteMockServe } from "vite-plugin-mock";

export default defineConfig({
  plugins: [
    vue(),
    viteMockServe({
      // mock 文件存放路径
      mockPath: "./mock",
      // 启用 mock 功能
      enable: true,
      // 显示请求日志
      logger: true,
    }),
  ],
  resolve: {
    alias: {
      "@": resolve(__dirname, "./src"),
    },
  },
});

配置 TypeScript

tsconfig.app.json 文件中配置路径别名,确保 TypeScript 能够正确解析 @/ 路径:

json 复制代码
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "paths": {
      "@/*": ["./src/*"]
    },
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

配置 Mock 数据

快速开始 - 最简单的用例

模拟第一个简单接口

第一步:创建最简单的 Mock 接口

mock 文件夹下创建一个简单的 hello.ts 文件:

typescript 复制代码
// mock/hello.ts
import { MockMethod } from "vite-plugin-mock";

export default [
  {
    url: "/api/hello",
    method: "get",
    response: () => {
      return {
        code: 200,
        message: "success",
        data: "Hello, Mock!",
      };
    },
  },
] as MockMethod[];
第二步:在组件中调用
vue 复制代码
<template>
  <div>
    <h2>{{ message }}</h2>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import axios from "axios";

const message = ref("");

onMounted(async () => {
  try {
    const response = await axios.get("/api/hello");
    message.value = response.data.data;
  } catch (error) {
    console.error("请求失败:", error);
  }
});
</script>

运行结果:页面显示 "Hello, Mock!"


中级用例 - 简单数据列表

这个用例展示如何返回一个简单的数据列表。

创建 Mock 接口

mock 文件夹下创建 products.ts 文件:

typescript 复制代码
// mock/products.ts
import { MockMethod } from "vite-plugin-mock";

export default [
  {
    url: "/api/products",
    method: "get",
    response: () => {
      return {
        code: 200,
        message: "success",
        data: [
          { id: 1, name: "商品1", price: 99 },
          { id: 2, name: "商品2", price: 199 },
          { id: 3, name: "商品3", price: 299 },
        ],
      };
    },
  },
] as MockMethod[];
在组件中调用
vue 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }} - ¥{{ product.price }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import axios from "axios";

interface Product {
  id: number;
  name: string;
  price: number;
}

const products = ref<Product[]>([]);

onMounted(async () => {
  try {
    const response = await axios.get("/api/products");
    products.value = response.data.data;
  } catch (error) {
    console.error("获取商品列表失败:", error);
  }
});
</script>

运行结果:页面显示商品列表,包含3个商品的信息。


进阶用例 - 用户数据管理

接下来,我们创建一个更完整的贴合业务场景的用例。

创建 Mock 文件

创建一个 user.ts 文件来模拟用户相关的接口:

typescript 复制代码
// mock/user.ts
import { MockMethod } from "vite-plugin-mock";

export default [
  {
    url: "/api/user/list", // 接口路径
    method: "get", // 请求方法
    response: ({ query }: { query: Record<string, string> }) => {
      // 模拟分页数据
      const page = parseInt(query.page) || 1;
      const limit = parseInt(query.limit) || 10;
      const total = 100;

      // 生成模拟数据
      const list = [];
      for (let i = 0; i < limit; i++) {
        const index = (page - 1) * limit + i;
        if (index < total) {
          list.push({
            id: index + 1,
            name: `用户${index + 1}`,
            age: Math.floor(Math.random() * 30) + 18,
            email: `user${index + 1}@example.com`,
            createdAt: new Date().toISOString(),
          });
        }
      }

      return {
        code: 200,
        message: "success",
        data: {
          list,
          total,
          page,
          limit,
        },
      };
    },
  },
  {
    url: "/api/user/detail",
    method: "get",
    response: ({ query }: { query: Record<string, string> }) => {
      const id = query.id;

      return {
        code: 200,
        message: "success",
        data: {
          id,
          name: `用户${id}`,
          age: Math.floor(Math.random() * 30) + 18,
          email: `user${id}@example.com`,
          createdAt: new Date().toISOString(),
          address: "北京市朝阳区",
          phone: "13800138000",
        },
      };
    },
  },
  {
    url: "/api/user/create",
    method: "post",
    response: ({ body }: { body: Record<string, any> }) => {
      return {
        code: 200,
        message: "success",
        data: {
          id: Math.floor(Math.random() * 10000),
          ...body,
        },
      };
    },
  },
] as MockMethod[];

调用 Mock 数据

mock数据的调用使用 axios 实现,本文暂不做过多覆盖。

创建 API 服务

src/api 目录下创建 user.ts 文件,定义 API 调用函数:

typescript 复制代码
// src/api/user.ts
import axios from "axios";

/**
 * 获取用户列表
 * @param params 分页参数
 * @returns Promise 响应数据
 */
export const getUserList = (params: { page: number; limit: number }) => {
  return axios.get("/api/user/list", { params });
};

/**
 * 获取用户详情
 * @param id 用户ID
 * @returns Promise 响应数据
 */
export const getUserDetail = (id: number) => {
  return axios.get("/api/user/detail", { params: { id } });
};

/**
 * 创建用户
 * @param data 用户数据
 * @returns Promise 响应数据
 */
export const createUser = (data: {
  name: string;
  age: number;
  email: string;
}) => {
  return axios.post("/api/user/create", data);
};

在组件中使用

在 Vue 组件中使用 API 服务调用 Mock 数据:

vue 复制代码
<template>
  <div class="user-list">
    <h2>用户列表</h2>
    <div v-if="loading">加载中...</div>
    <div v-else>
      <ul>
        <li v-for="user in userList" :key="user.id">
          {{ user.name }} - {{ user.age }}岁 - {{ user.email }}
        </li>
      </ul>
      <div class="pagination">
        <button @click="changePage(1)" :disabled="currentPage === 1">
          首页
        </button>
        <button
          @click="changePage(currentPage - 1)"
          :disabled="currentPage === 1"
        >
          上一页
        </button>
        <span>第 {{ currentPage }} 页,共 {{ totalPages }} 页</span>
        <button
          @click="changePage(currentPage + 1)"
          :disabled="currentPage === totalPages"
        >
          下一页
        </button>
        <button
          @click="changePage(totalPages)"
          :disabled="currentPage === totalPages"
        >
          末页
        </button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { getUserList } from "@/api/user";

// 响应式数据
const userList = ref<any[]>([]);
const loading = ref(false);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);

// 计算属性
const totalPages = computed(() => {
  return Math.ceil(total.value / pageSize.value);
});

/**
 * 获取用户列表数据
 */
const fetchUserList = async () => {
  loading.value = true;
  try {
    const response = await getUserList({
      page: currentPage.value,
      limit: pageSize.value,
    });
    userList.value = response.data.data.list;
    total.value = response.data.data.total;
  } catch (error) {
    console.error("获取用户列表失败:", error);
  } finally {
    loading.value = false;
  }
};

/**
 * 切换页码
 * @param page 页码
 */
const changePage = (page: number) => {
  currentPage.value = page;
  fetchUserList();
};

// 组件挂载时获取数据
onMounted(() => {
  fetchUserList();
});
</script>

常见用例和最佳实践

常见用例

  1. 分页数据:模拟带分页的列表数据

  2. 详情数据:模拟单个资源的详细信息

  3. 表单提交:模拟创建、更新操作

  4. 错误场景:模拟各种错误状态码和错误信息

  5. 文件上传:模拟文件上传接口

最佳实践

  1. 目录结构清晰:按模块组织 mock 文件

  2. 数据结构一致:与后端接口保持一致的数据结构

  3. 模拟真实场景:包括正常、异常、边界等各种场景

  4. 使用 TypeScript:为 mock 数据添加类型定义

  5. 注释完善:为复杂的 mock 逻辑添加注释

  6. 定期更新:根据后端接口变化及时更新 mock 数据

故障排除提示

  1. mock 数据不生效
  • 检查 vite.config.ts 中的 mockPath 配置是否正确

  • 检查 mock 文件是否在正确的目录下

  • 检查接口路径是否匹配

  1. TypeScript 类型错误
  • 确保安装了 @types/mockjs 类型定义

  • 为 mock 数据添加正确的类型注解

  1. 生产环境泄露
  • 确保在生产环境中禁用 mock 功能
  1. 性能问题
  • 避免在 mock 函数中执行复杂的计算

  • 对于大量数据,考虑使用分页或虚拟滚动

常见问题

1. 路径别名问题

问题:找不到模块 "@/api/user" 或其相应的类型声明。

原因 :虽然在 vite.config.ts 中配置了路径别名,但在 tsconfig.app.json 中没有配置相应的 paths

解决方案 :在 tsconfig.app.json 中添加 paths 配置:

json 复制代码
{
  "compilerOptions": {
    // ... 其他配置
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

2. 接口实现不完整

问题:调用某个 API 函数时返回 404 错误。

原因:在 mock 文件中没有实现对应的接口。

解决方案:确保所有 API 调用都有对应的 mock 接口实现。

3. 类型定义缺失

问题:使用 mockjs 时缺少类型定义。

原因 :没有安装 @types/mockjs 类型定义文件。

解决方案:安装类型定义文件:

bash 复制代码
npm install --save-dev @types/mockjs

替代方案比较

Mock.js vs JSON Server

  • Mock.js:专注于数据模拟,功能强大,支持各种数据类型和随机数据生成

  • JSON Server:快速创建 RESTful API,基于 JSON 文件,适合简单场景

Mock.js vs MSW (Mock Service Worker)

  • Mock.js:在构建工具层面拦截请求,配置简单

  • MSW:在浏览器层面拦截请求,支持 Service Worker,更接近真实网络请求

Mock.js vs 手写本地存储

  • Mock.js:功能完整,支持各种 HTTP 方法和场景

  • 手写本地存储:简单直接,适合非常简单的场景

注意事项

  1. 环境隔离:确保 mock 功能只在开发和测试环境启用

  2. 数据安全:不要在 mock 数据中使用真实的敏感信息

  3. 接口一致性:与后端保持接口规范一致,避免后期大量修改

  4. 代码管理:将 mock 相关代码与业务代码分离,便于维护

  5. 性能考虑:避免生成过多数据,影响前端性能

  6. 测试覆盖:确保真实接口对接后,进行完整的回归测试

总结

Mock 数据是前端开发中的重要工具,它可以帮助我们提高开发效率,增强代码健壮性,改善团队协作。

「九九八十一难,难难皆是修行」

相关推荐
Zhencode2 小时前
Vue3 响应式依赖收集与更新之effect
前端·vue.js
x-cmd2 小时前
[x-cmd] jsoup 1.22.1 版本发布,引入 re2j 引擎,让 HTML 解析更安全高效
前端·安全·html·x-cmd·jsoup
天下代码客2 小时前
使用electronc框架调用dll动态链接库流程和避坑
前端·javascript·vue.js·electron·node.js
weixin199701080163 小时前
【性能提升300%】仿1688首页的Webpack优化全记录
前端·webpack·node.js
冰暮流星3 小时前
javascript之数组
java·前端·javascript
晚霞的不甘3 小时前
Flutter for OpenHarmony天气卡片应用:用枚举与动画打造沉浸式多城市天气浏览体验
前端·flutter·云原生·前端框架
xkxnq3 小时前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
三小河4 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
Hilaku4 小时前
不要在简历上写精通 Vue3?来自面试官的真实劝退
前端·javascript·vue.js