Mock Data

Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发更加独立自主,不会被服务端的开发所阻塞。

Swagger

在公司的项目中通常使用 swaggerMock Data - 图1, 由后端来模拟业务数据。 swagger 是一个 REST APIs 文档生成工具,它从代码注释中自动生成文档,可以跨平台,开源,支持大部分语言,社区好,总之非常不错,强烈推荐。 线上 demoMock Data - 图2

Easy-Mock

vue-admin-templateMock Data - 图3 之前使用的是 easy-mockMock Data - 图4 来模拟数据。 它是一个纯前端可视化,并且能快速生成模拟数据的持久化服务。非常的简单易用还能结合 swagger,天然支持跨域 ,不管团队还是个人项目都值得一试。

WARNING

现在线上版本的vue-admin-template 已经不使用easy-mock。因为easy-mock提供的线上免费服务很不稳定,时不时的就会挂掉,如果有需要的可以自己按照它的教程,搭建自己的服务。

Mockjs

由于 vue-element-adminMock Data - 图5 是一个纯前端个人项目,所有的数据都是用 mockjsMock Data - 图6 模拟生成。它的原理是: 拦截了所有的请求并代理到本地,然后进行数据模拟,所以你会发现 network 中没有发出任何的请求。

但它的最大的问题是就是它的实现机制。它会重写浏览器的XMLHttpRequest对象,从而才能拦截所有请求,代理到本地。大部分情况下用起来还是蛮方便的,但就因为它重写了XMLHttpRequest对象,所以比如progress方法,或者一些底层依赖XMLHttpRequest的库都会和它发生不兼容,可以看一下我项目的issuesMock Data - 图7,就知道多少人被坑了。

它还有一个问题是,因为是它本地模拟的数据,实际上不会走任何网络请求。所以本地调试起来很蛋疼,只能通过console.log来调试。就拿vue-element-admin来说,想搞清楚 getInfo()接口返回了什么数据,只能通过看源码或者手动 Debug 才能知道。

新方案 v4.0.0+

v4.0版本之后,在本地会启动一个mock-server来模拟数据,线上环境还是继续使用mockjs来进行模拟(因为本项目是一个纯前端项目,你也可以自己搭建一个线上 server 来提供数据)。不管是本地还是线上所有的数据模拟都是基于mockjs生成的,所以只要写一套 mock 数据,就可以在多环境中使用。

该方案的好处是,在保留 mockjs的优势的同时,解决之前的痛点。由于我们的 mock 是完全基于webpack-dev-serve来实现的,所以在你启动前端服务的同时,mock-server就会自动启动,而且这里还通过 chokidarMock Data - 图8 来观察 mock 文件夹内容的变化。在发生变化时会清除之前注册的mock-api接口,重新动态挂载新的接口,从而支持热更新。有兴趣的可以自己看一下代码mock-server.jsMock Data - 图9。由于是一个真正的server,所以你可以通过控制台中的network,清楚的知道接口返回的数据结构。并且同时解决了之前mockjs会重写 XMLHttpRequest对象,导致很多第三方库失效的问题。

本项目的所有请求都是通过封装的request.jsMock Data - 图10进行发送的,通过阅读源码可以发现所有的请求都设置了一个baseURL,而这个baseURL又是通过读取process.env.VUE_APP_BASE_API这个环境变量来动态设置的,这样方便我们做到不同环境使用不同的 api 地址。

移除

如果你不想使用mock-server的话只要在vue.config.jsMock Data - 图11中移除webpack-dev-serverproxyafter这个Middleware就可以了。

现在默认情况下本地的请求会代理到http://localhost:${port}/mock下,如果你想调整为自己的 mock 地址可以修改 proxy

  1. proxy: {
  2. // change xxx-api/login => mock/login
  3. // detail: https://cli.vuejs.org/config/#devserver-proxy
  4. [process.env.VUE_APP_BASE_API]: {
  5. target: `http://localhost:${port}/mock`,
  6. changeOrigin: true,
  7. pathRewrite: {
  8. ['^' + process.env.VUE_APP_BASE_API]: ''
  9. }
  10. }
  11. },
  12. after: require('./mock/mock-server.js')

TIP

请注意:该操作需要重启服务

mock-server只会在开发环境中使用,线上生产环境目前使用MockJs进行模拟。如果不需要请移除。具体代码:main.jsMock Data - 图12

  1. import { mockXHR } from '../mock'
  2. if (process.env.NODE_ENV === 'production') {
  3. mockXHR()
  4. }

新增

如果你想添加 mock 数据,只要在根目录下找到mock文件,添加对应的路由,对其进行拦截和模拟数据即可。

比如我现在在src/api/articleMock Data - 图13中需要添加一个查询某篇文章下面评论数的接口fetchComments,首先新建接口:

  1. export function fetchComments(id) {
  2. return request({
  3. url: `/article/${id}/comments`,
  4. method: 'get'
  5. })
  6. }

声明完接口之后,我们需要找到对应的 mock 文件夹mock/article.jsMock Data - 图14,在下面创建一个能拦截路由的 mock 接口

请注意,mock 拦截是基于路由来做的,请确保 mock 数据一定能匹配你的 api 路由,支持正则

  1. // fetchComments 的 mock
  2. {
  3. // url 必须能匹配你的接口路由
  4. // 比如 fetchComments 对应的路由可能是 /article/1/comments 或者 /article/2/comments
  5. // 所以你需要通过正则来进行匹配
  6. url: '/article/[A-Za-z0-9]/comments',
  7. type: 'get', // 必须和你接口定义的类型一样
  8. response: (req, res) => {
  9. // 返回的结果
  10. // req and res detail see
  11. // https://expressjs.com/zh-cn/api.html#req
  12. return {
  13. code: 20000,
  14. data: {
  15. status: 'success'
  16. }
  17. }
  18. }
  19. }

修改

最常见的操作就是:你本地模拟了了一些数据,待后端完成接口后,逐步替换掉原先 mock 的接口。

我们以src/api/role.jsMock Data - 图15中的getRoles接口为例。它原本是在mock/role/index.jsMock Data - 图16中 mock 的数据。现在我们需要将它切换为真实后端数据,只要在mock/role/index.jsMock Data - 图17找到对应的路由,之后将它删除即可。这时候你可以在network中,查看到真实的数据。

  1. // api 中声明的路由
  2. export function getRoles() {
  3. return request({
  4. url: '/roles',
  5. method: 'get'
  6. })
  7. }
  8. //找到对应的路由,并删除
  9. {
  10. url: '/roles',
  11. type: 'get',
  12. response: _ => {
  13. return {
  14. code: 20000,
  15. data: roles
  16. }
  17. }
  18. },

多个 server

目前项目只启动了一个mock-server,当然你也可以有自己其它的mock-server或者代理接口。可以一部分接口走这个服务,另一些接口走另一个服务。只需要将它们分别设置不同的的baseURL即可。 @/utils/request.jsMock Data - 图18

之后根据设置的 url 规则在 vue.config.jsMock Data - 图19 中配置多个 proxy

相关文档Mock Data - 图20

启用纯前端 Mock

现在在mock/index.jsMock Data - 图21也封装了一个纯前端 mock 的方法,你只需要在src/main.jsMock Data - 图22中:

  1. import { mockXHR } from '../mock'
  2. mockXHR()

这样就会变成纯前端 mock 数据了和v4.0版本之前的 mock 方案是一样的,原理见上文。目前你看到的线上demoMock Data - 图23就是采用该种方式。

本地 Mock 数据与线上数据切换

有很多时候我们会遇到本地使用 mock 数据,线上环境使用真实数据,或者说不同环境使用不同的数据。

  • Easy-Mock 的形式

你需要保证你本地模拟 api 除了根路径其它的地址是一致的。 比如:

  1. https://api-dev/login // 本地请求
  2. https://api-prod/login // 线上请求

我们可以通过之后会介绍的环境变量来做到不同环境下,请求不同的 api 地址。

  1. # .env.development
  2. VUE_APP_BASE_API = '/dev-api' #注入本地 api 的根路径
  1. # .env.production
  2. VUE_APP_BASE_API = '/prod-api' #注入线上 api 的根路径

之后根据环境变量创建axios实例,让它具有不同的baseURL@/utils/request.jsMock Data - 图24

  1. // create an axios instance
  2. const service = axios.create({
  3. baseURL: process.env.BASE_API, // api 的 base_url
  4. timeout: 5000 // request timeout
  5. })

这样我们就做到了自动根据环境变量切换本地和线上 api。

  • Mock.js 的切换

当我们本地使用 Mock.js 模拟本地数据,线上使用真实环境 api 方法。这与上面的 easy-mock 的方法是差不多的。我们主要是判断:是线上环境的时候,不引入 mock 数据就可以了,只有在本地引入 Mock.js

  1. // main.js
  2. // 通过环境变量来判断是否需要加载启用
  3. if (process.env.NODE_ENV === 'development') {
  4. require('./mock') // simulation data
  5. }

只有在本地环境之中才会引入 mock 数据。