在Vue项目中使用Easy Mock模拟开发接口

5,293 阅读8分钟

前言


马上就要做一次关于Easy Mock的培训,在这里对它做一个总结记录吧。
PS:建议直接看如何在项目中使用。

Easy Mock 简介

伪造数据,我们更高效
但,不仅于此

Easy Mock 是一个可视化,并且能快速生成 模拟数据 的持久化服务。

在下图中,我们可以看到 Easy Mock 首页给出的介绍(这里插一嘴,人家首页做的是真漂亮!):

image.png

那对于我们来说,什么时候使用呢?

前后端分离项目,工作进度又快,后端出接口的速度很大程度上会影响到我们前端的开发进度。这时候只要接口已经定义好了,我们就可以使用 Easy-mock 模拟接口返回数据来辅助我们进行功能的开发。等后端接口实现完成后,再进行联调。

Easy Mock 项目及接口创建

PS:这块可以不看,建议直接上 Easy Mock 自己动手尝试。

新建项目
  • 进入个人项目这里,然后新建一个自己的测试项目

image.png

新建接口
  • 点击刚才新建的项目进入接口列表页面

image.png

  • 点击创建接口,选择接口Method,填写URL和描述,左侧内容就是该接口返回的内容,见下面示例

image.png

// 这是一个登录的post请求示例,登录成功失败随机
{
  "data": function() {
    return this.meta.code === 1 ? {
      token: (new Date().getTime())
    } : null
  },
  "meta": {
    'code|1': [0, 0, 1],
    message: function() {
      return this.code === 1 ? '登录成功' : '用户名或密码错误,请重新输入'
    }
  }
}

Easy Mock 响应式数据

看到上边的示例,你可能会有疑问,你这个是随机的结果,如果我想根据用户输入的来控制返回数据呢?别急,Easy Mock 已经替我们想过了!

我们来看首页关于响应式的介绍

响应式数据
要数据联动那是不可能了,不过 Easy Mock 支持响应式数据。通过判断入参返回对应的数据。

阅读这段话,来划重点了!
通过判断入参返回对应的数据,是的,跟你想的一样一样的,就是通过入参来判断。
那我们接下来看看文档是怎么描述的。

我们可以在 数据编辑器 中,为某个属性指定一个 Function。在 Function 中,我们提供了  _req 对象,这使得我们可以通过请求对象编写逻辑,实现响应式数据,如图所示。

{
  success: true,
  data: {
    default: "hah",
    _req: function({
      _req
    }) {
      return _req
    },
    name: function({
      _req,
      Mock
    }) {
        if(_req.query.name === 'nk'){
            return _req.query.name + '_' + Mock.mock('@name')
        } else {
            return this.default
        }
    }
  }
}

也就是说,我们可以通过使用 Function ,对 Function 参数进行处理来获得我们想要的数据。

这里我列几个我们最常用的,满足一下讲解需要,具体内容还是要 看详细文档 的。

  • Mock Mock 对象
  • _req.params 获取 url 参数对象
  • _req.query 将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字字符串则返回一个空对象
  • _req.body 当 post 请求以 x-www-form-urlencoded 方式提交时,我们可以拿到请求的参数对象

看到这里我们来将上面的登录接口修改成只有用户名是admin,密码是123才能登录,其他一律返回错误数据

{
  "data": function({
    _req,
    Mock
  }) {
    const {
      username,
      password
    } = _req.body
    if (username === 'admin' && password === '123') {
      return {
        token: (new Date().getTime())
      }
    }
    return null
  },
  "meta": {
    code: function({
      _req,
      Mock
    }) {
      const {
        username,
        password
      } = _req.body
      if (username === 'admin' && password === '123') {
        return 1
      }
      return 0
    },
    message: function() {
      return this.code === 1 ? '登录成功' : '用户名或密码错误,请重新输入'
    }
  }
}

来更新一下我们的接口,然后点击预览接口。
可以看到,这个登录已经实现了我们上文提到的效果

image.png

更多的玩法可自行研究

Easy Mock 的 Mock.js

Easy Mock 内置了 Mock.js,我们可以更愉快的伪造数据了。

PS:下边是举的几个例子,具体还是得看 mock.js 官网,样例最多最全,还可以打开发者工具进行调试,这里就不做更多的说明了。

常用语法如下:

"string|1-10": "★",  // 生成1-10个★
"integer": "@integer(10, 30)", // 生成10-30之间的数值
"date": "@date(yyyy-MM-dd)", // 随机日期 - date: "2015-09-04"
"datetime": "@datetime", // 随机时间 - datetime: "1971-01-05 18:55:33"
"url": "@url",
"email": "@email", 
"city": "@city", 
"county": "@county", 
"image": "@image(200x200)", 
"title": "@title",

Easy Mock 在项目中如何使用

重点来了,这个模块是本文的重点,因为本文是个人手打,纯个人理解,如果哪里我说的不对,或者解释模糊存在疑问的地方,还请您一定要指出来,共同学习进步。

  • vue-cli 项目 这个简单,适用学习者,代码直接搬运即可。
  • vue + egg.js 项目 这个是公司项目组成,不一定适用每个人,但我希望对你有所帮助。

vue-cli 项目

  • 先将我们刚才新建项目的 Base Url 复制过来,这个相当于我们请求的服务器。
  • 下方是我们每个接口的请求路径,见 api.js 文件。

image.png

  1. 新增 vue.config.js 文件。
const path = require('path');

module.exports = {
  devServer: {
    // 自动打开页面
    open: true,
    proxy: {
      '/mock': {
        target: 'https://mock.mengxuegu.com/mock/60e656f3fd08e7028e4b926a',
        // 是否允许代理(开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题)
        changeOrigin: true,
        // 地址重启(对请求路径进行重定向以匹配到正确的请求地址)
        pathRewrite: {
          '^/mock': '/mock' // 实际请求路径     http://localhost:8080/mock/user/login
          // "^/mock": "/", // 实际请求路径    http://localhost:8080/user/login
        }
      }
    }
  },
  configureWebpack: {
    resolve: {
      // 路径别名
      alias: {
        '@': path.join(__dirname, './src')
      }
    }
  }
};

  1. 接下来,写一下我们的 api.js 文件(请求接口)。
// 这个文件是我的axios封装文件,想必大家都懂,这里就不放了。
import request from '@/utils/request.js'

// 登录验证
export function getUserList(data) {
    // 将 Easy Mock 对应接口URL复制过来
    return request({
        url: '/mock/user/login',
        method: 'post',
        data
    })
}
  1. 在项目中使用
import { getUserList } from '@/api.js';

// 调用接口
async getUserList() {
  this.loading = true;
  const params = {
      username: 'admin',
      password: '123'
  }
  const res = await getUserList(params);
  const { meta, data } = res;
  if (meta && meta.code === 1) {
      // TODO 成功处理
  } else {
      // TODO 失败处理
  }
  this.loading = false;
}

vue + egg.js 项目

PS:再次声明,每个公司的项目配置不一定相同,但一定有借鉴的地方。接下来介绍仅针对本公司项目配置。

项目中使用了两种请求方式,两种请求各有千秋。

// 封装好的 axios 请求
import HttpRequest from '../utils/request';

export default class BaseApi {
  
  static request(options) {
    return HttpRequest(options);
  }

  static proxy(proxyName, options) {
    // 注意这个/proxy,会在路由中拦截进行特殊处理
    options.url = `/proxy/${proxyName}${options.url}`;
    return HttpRequest({
      ...options,
      ...{
        proxy: proxyName
      }
    });
  }

  static get proxyName() {
    return process.env.proxyName;
  }
}
  • proxy 代理请求

优点:不需要再配置router和controller,减少代码量

  • request 请求

优点:需要配置router和controller,功能方面更加灵活。


proxy 代理请求

我们先来看一下proxy方法如何在接口中使用的

import BaseApi from '@framework/api/base';

export default class ManageApi extends BaseApi {
  static getRankData() {
    // 这里的 this.proxyName 就是 process.env.proxyName
    return this.proxy(this.proxyName.testMock, {
      url: '/mock/xxx',
      method: 'post',
    });
  }
}

注意:代码中的 this.proxyName.testMock,其中this.proxyName 就是上文中的 process.env.proxyName

接下来我们看一下路由是怎么拦截的

router.all(`${BASE_PATH}/proxy/*`, controller.proxy.index);

路由匹配所有以/proxy开头的请求地址,然后通过 controller.proxy.index 解析用户的输入,处理后返回相应的结果。

走到这里,我们需要了解以下两点

  1. process.env.proxyName 是如何配置的(这个相信大家都知道)?
  2. controller下的proxy文件中的index方法做了什么处理?
  • 我们先来看一下process.env.proxyName的配置

在 build 文件夹的 env.dev.js 文件中添加 mock 服务代理(不允许添加到 Apollo/测试/生产环境)

const Extend = require('extend');
const DefaultENV = require('./env.default');

module.exports = Extend(true, DefaultENV, {
  // 这个就是接口配置的mock代理字段
  proxyName: {
    testMock: 'test_mock'
  }
});
  • build 是 webpack 的配置文件

这里又延伸出一点
proxyName 配置的字段是如何代理mock服务的,先记下,下文做了解释。

  • 接下来我们再看看controller下的proxy文件中的index方法做了什么处理。
// 简略代码
async index() {
    const { ctx, service } = this;
    const result = await service.proxyapi.index();
    // 取出响应头
    const resContentType = result.headers['content-type'];
    
    // 根据响应头判断返回值是json格式,否则为字节流
    if (resContentType.indexOf('application/json') > -1) {
      // ... 下方省略如何处理的
    } else {
      // 处理下载文件
      ctx.set('Content-Disposition', result.res.headers['content-disposition']);
      ctx.set('Content-Type', resContentType);
      ctx.body = result.data;
      ctx.status = result.status;
    }
}

这里的代码重点是这一行const result = await service.proxyapi.index();

所以,重点是service.proxyapi.index是如何代理服务的。

提起精神来,马上就结束了要~~~

来,我们看一下service下proxyapi文件中的index方法是如何处理的。

// 简略代码
async index(params = {}) {
    const { ctx } = this;
    // 假设我们的 path: /proxy/testMock/user/login
    // match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
    const urlRegResult = ctx.request.path.match(/\/proxy(.*)/);
    const [
      ,
      regUrl
    ] = urlRegResult;
    // regUrl: /testMock/user/login
    
    // exec() 方法用于检索字符串中的正则表达式的匹配。
    const pathNode = PathToRegexp('/:apiName/:proxyURL*').exec(regUrl);
    const [
      ,
      apiName, proxyURL
    ] = pathNode;
    
    // 看这两行!!!
    const api = ctx.app.config.api || {};
    const apiBaseURL = api[apiName];

    if (!apiBaseURL) {
      // TODO 路径不存在的处理
    }
    
    // 这里的url 就是我们实际的请求地址
    const url = `${apiBaseURL}/${proxyURL}`;
    const result = await this.proxyRequest(url, params);
    
    return result;
  }

重点来了!!!

看这两行代码!!!

const api = ctx.app.config.api || {};
const apiBaseURL = api[apiName];

上文我们留下了一个疑问,即 proxyName 配置的字段是如何代理mock服务的
在这里我们得到了解释,我们从ctx.app.config.api中将proxyName对应的请求地址取出来,组成最终的请求地址,即${apiBaseURL}/${proxyURL}

好的,这个问题我们已经得到了解释。
接下来,我们只需要在ctx.app.config.api配置我们的请求地址即可。

  • config 是 node server 的配置文件
// 简略代码
module.exports = (app) => {
  const config = {};
  config.api = {
    // 这里我们配置了test_mock 对应的请求地址
    test_mock:
    // 这里需要写你需要的mock地址
      'http://testmock.mock.com/mock/1e232f0bd0c4a41f5447df52',
  };
};

OK,到此我们使用 proxy 方法的介绍已经完毕。

request 请求
  • 方法2,使用的request方法(发送的请求会被node端拦截,所以我们需要配置好router以及controller),更简单些

首先我们还是看一下api方法是如何写的。

// 简略代码
static getAllMyData(data) {
  return this.request({
    method: 'post',
    url: `/mock/getAllMyData`,
    data
    });
  }
}

request方法需要配置路由

router.post(`/mock/getAllMyData`, controller.testData.getAllMyData);

然后在controller下的TestData文件中配置getAllMyData方法

// 简略代码
async getAllMyData() {
    const { ctx } = this;
    // 看这个请求路径 ctx.app.config.api.testMock
    const url = `${ctx.app.config.api.testMock}/mock/getAllMyData`;
    await this.proxyRequest(url);
}

在这里我们可以发现,我们的请求路径是ctx.app.config.api.testMock,有没有很熟悉的感觉, 没错,就是刚才我们配置的config文件。

好了,到此request的请求方式也介绍完了。

有需要的小伙伴,下来可以自己动手操作一遍了。

mock地址改为测试站地址

假设,现在后端已经开发完毕,前后端需要联调了,我们需要做的事情如下:

  • proxy 代理请求:需要在build文件中添加测试地址对应名称,然后将api文件中this.proxyName.xxx修改为测试地址对应名称即可。
  • request 请求: 将controller中的config.api.xxx修改为测试地址对应名称。

注意:如果config文件中没有配置测试地址对应名称,项目会自动去apollo.json文件中找。

补充

还有一些其他可以mock数据的方式,列举几个:

  • jsonS-server 在本地起一个虚拟服务器,将数据存储到本身json文件中
  • Mock.js 在项目中会对接口请求进行拦截,返回本地 mock 数据
  • YApi 强烈推荐使用,高效、易用、功能强大的API管理平台,具有接口分组的优势。点我直达
  • rap2 类似Easy Mock,也是一个线上平台。点我直达

参考

最后


  • 文章是自己手敲,是对工作日常的总结,如有错误之处,敬请指正
  • 如果遇到什么问题就留言吧,能解决大家帮忙一起解决一下