方案:Vue 集成 Mock.js

3,620 阅读4分钟

关联标签: #vue #vite #mock #yapi #easymock #前端

背景

项目启动前期,后端同学只提供了接口文档,前端同学为了使功能流程跑通,需要根据文档去 mock 数据。

技术调研

如何优雅的 mock 数据,是通过云端平台 yapi、easy-mock,还是本地 mock 方案。目前使用个人搭建的 easy-mock 服务。

EasyMock

  • 官网:easy-mock/easy-mock at master (github.com)
  • 优点:
    • 支持内网部署
    • 支持接口代理
    • 支持 Swagger && OpenAPI 导入
    • 支持 Mock.js 语法
    • 支持 restc 接口预览
    • 支持自定义响应配置
    • 支持团队管理
  • 缺点:
    • 不支持文档
    • 云端部署

YApi

  • 官网:github.com/ymfe/yapi
  • 优点:
    • 支持内网部署
    • 支持自动化测试
    • 支持类似 postman 接口调试
    • 扁平化权限管理
    • 支持 Mock.js 语法
    • 支持自定义插件
    • 支持参数和响应格式预览
  • 缺点:
    • 需要云端部署

本地 mock

  • 官网:Mock.js (mockjs.com)
  • 优点:
    • 跟随项目代码
    • 支持 js 语法,可随意定制请求和响应
    • 可跟随 git,有版本控制
  • 缺点
    • 项目外的人员无法查看

项目实践

目前个人使用 easy-mock 搭建云端服务,比较繁琐的事情是没有部署到公司里,无法将后端的 swagger 文档给同步过来,最后还是决定采用本地 mock 方案,将 mock 代码放入项目 git 仓库,每个人去开发自己功能的时候,自己创建个人的 mock 文件。

安装依赖

目前团队项目使用 vue 框架 2.x 版本,vite 2.x 版本构建,pnpm 进行包管理。

需要再命令行中安装如下依赖

# mockjs 用于生成 mock 数据
# vite-plugin-mock 是 vite 的插件
pnpm i mockjs vite-plugin-mock -D

项目配置

更新 vite.config.js

import { createVuePlugin } from 'vite-plugin-vue2';
import { viteMockServe } from 'vite-plugin-mock';

// https://vitejs.dev/config/
export default ({ command }) => {
  console.log('current command is ', command);
  return {
    resolve: {
      alias: [{ find: '@', replacement: '/src' }],
    },
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "@/assets/style/index.scss";`,
        },
      },
    },
    plugins: [
      createVuePlugin(),
      viteMockServe({
        ignore: /^\_/, // 以 _ 开头的是工具文件,忽略
        mockPath: 'mock', // 存放在 mock 目录下
        prodEnabled: false, // 关闭生产环境 mock
      }),
    ],
  };
};

项目根目录创建 mock 文件夹,同时创建 mock/_utils.ts 工具方法文件

// 成功响应
export function success<T = Recordable>(data: T, msg = 'success') {
  return {
    code: 0,
    data,
    msg,
  };
}

// 列表成功响应
export function listSuccess<T = any>(
  page: number,
  limit: number,
  list: T[],
  { msg = 'success' } = {}
) {
  const pageData = pagination(page, limit, list);

  return {
    ...success({
      list: pageData,
      total: list.length,
    }),
    msg,
  };
}

// 错误响应,错误码前2位为业务分类,后4位为业务内部错误
// 1 开头的错误为网关错误和通用认证错误,业务侧不得声明为 1 开头的错误码
export function fail(msg = 'mock 错误', code = 100000, data = null) {
  return {
    code,
    data,
    msg,
  };
}

// 分页方法
export function pagination<T = any>(
  page: number,
  limit: number,
  list: T[]
): T[] {
  const offset = (page - 1) * Number(limit);
  const ret =
    offset + Number(limit) >= list.length
      ? list.slice(offset, list.length)
      : list.slice(offset, offset + Number(limit));
  return ret;
}

// 请求参数类型
export interface requestParams {
  method: string;
  body: any;
  headers?: { authorization?: string };
  query: any;
}

// 获取请求头认证 token
export function getRequestToken({
  headers,
}: requestParams): string | undefined {
  return headers?.authorization;
}

创建 mock/demo.ts 文件,mock 一个增删改查的接口

import { MockMethod } from 'vite-plugin-mock';
import Mock from 'mockjs';
import { success, fail, listSuccess, requestParams } from '../_utils.ts';

// 自定义手机号 mock
Mock.Random.extend({
  phone() {
    return Mock.mock(/1[3456789]\d{9}/);
  },
});

export default [
  // 创建
  {
    url: '/demos/create',
    timeout: 1000,
    method: 'post',
    response({ body }) {
      return success({
        id: 123,
        createdAt: '@now',
        updatedAt: '@now',
        ...body,
      });
    },
  },
  // 删除
  {
    url: '/demos/delete',
    timeout: 1000,
    method: 'delete',
    response({ query }) {
      if (parseInt(query.id) === 1) {
        return fail('内容不存在');
      }
      return success(null, '删除成功');
    },
  },
  // 修改
  {
    url: '/demos/update',
    timeout: 1000,
    method: 'put',
    response({ query, body }) {
      return success({
        ...body,
        id: query.id,
        updatedAt: '@now',
      });
    },
  },
  // 查询列表
  {
    url: '/demos/list',
    timeout: 1000,
    method: 'get',
    response({ query }: requestParams) {
      const res: any = [];
      for (let i = 0; i < 88; i++) {
        res.push({
          id: i + 1,
          'type|1': ['管理员', '白名单'],
          'userId|100000-200000': 100,
          username: '@cname()',
          createdAt: '@datetime',
          updatedAt: '@now',
        });
      }
      return listSuccess(query.page, query.limit, res);
    },
  },
  // 查询详情
  {
    url: '/demos/detail',
    timeout: 1000,
    method: 'get',
    response({ query }) {
      return success({
        id: parseInt(query.id),
        name: '@cname',
        email: '@email',
        bio: '@cparagraph',
        phone: Mock.Random.phone(),
        createdAt: '@datetime',
        updatedAt: '@now',
      });
    },
  },
] as MockMethod[];

效果演示

image.png

Untitled 1.png

总结

目前团队前端项目和后端项目相对独立,没有串联,因此采用本地 Mock 的方式可以低成本、高定制化、高效的解决前端对接口依赖的问题。

相对于大型团队的项目来讲,如果公司的基础设施完善,那么可以优先考虑部署可定制化更高的 YApi 进行接口管理,将文档和其他工具流串联打通起来,从后端开始创建 Swagger → YApi → 前端 Mock → 测试接口自动化 → 产品查阅接口文档,当整个链路全部打通,创建标准化开发流程,会大大提高研发效率,降低因接口产生的沟通成本。

参考文档

  1. Mock.js (mockjs.com)
  2. github.com/anncwb/vite…
  3. github.com/anncwb/vue-…