搭建前端mock层

什么是mock?mock指的是模拟。前端

在软件工程中当开发工做不一样步状况下为了避免断工做链,而模拟其中一环节让工做得以持续下去,符合这种状况的工做咱们就叫mock。固然咱们常说的mock是指模拟网络请求先后端通信。vue

前端和后端的协同工做中,为了避免串行工做流加快开发效率,每每须要制定协议,而前端只须要后端提供数据接口,因此这份协议就是接口协议。根据这份协议前端就能够mock模仿数据,达到先后端并行开发。node

mock能够分为如下几种方式:webpack

  1. 侵入业务代码,设置开关
  2. 利用webpack构建工具拦截请求
  3. 搭建mock服务层代理响应
  4. 利用浏览器插件拦截请求

cms-web是由vue-cli3脚手架搭建的项目,依赖webpack打包工具,能够利用devServer.defore api对请求拦截处理,返回mock数据,引入mock层。git

因此选择上面第二种方式,缘由总结为:无侵入,简单快捷易上手github

mockjs

借助mockjs实现mock数据逻辑,如下是最简单的实现:web

var Mock = require('mockjs');
...
before(app, server) {
    app.get('/api/action/game/list', function (req, res, next) {
      var data = Mock.mock({
        code: 0,
        data: {
          'totalCount|1-100': 1,
          'datas|1-10': [{
            'id|+1': 1,
            'dataName|1-5': 'name',
            'dataType|1-2': 1,
            'dataScope': '1,100',
            'defaultValue|1-5': 'value',
            'dataStatus|0-1': 0,
            'remark|1-5': 'str',
            'record|0-1': 0,
            'series|1-3': [{
              'id|+1': 1,
              'type|1-2': 2,
              'name|1-2': '小云熊奇妙故事',
              'mark': null,
              'status': 0
            }]
          }]
        }
      })
    
      res.json(data)
    })
}
复制代码

这里咱们简单介绍一下mockjs的使用规范,详细移至mockjs文档ajax

数据模板中属性主要由三个部分构成:属性名,生成规则和属性值chrome

'属性名|生成规则': 属性值
复制代码

以'dataName|1-5': 'name'为例,dataName是属性值,name是属性值,1-5是生成规则,对应String类型属性表明重复随机次数1到5次,假如对应Number类型表明生成随机数的最小值1和最大值5。'id|+1': 1表示id值会从开始叠加。vue-cli

优化mock层

话题回来mock层搭建的迭代优化,首先分析上面实现存在的问题:

  • 全部的请求都会通过这一层,mock层匹配到请求就会被拦截,须要设置开关
  • 配置的请求和数据模板会愈来愈多,逻辑与配置混合会致使代码愈来愈臃肿,逻辑和配置要分离

把mock层从before函数中抽离,并设置开关机制:

var Mock = require('mockjs');

class MockHandler {
  constructor() {}
  
  handle(app, server) {
    app.get('/api/action/game/list', function (req, res, next) {
      if (!!process.env.mock || !!req.query._is_mock) {
        var data = Mock.mock({
          code: 0,
          data: {
            'totalCount|1-100': 1,
            'datas|1-10': [{
              'id|+1': 1,
              'dataName|1-5': 'name',
              'dataType|1-2': 1,
              'dataScope': '1,100',
              'defaultValue|1-5': 'value',
              'dataStatus|0-1': 0,
              'remark|1-5': 'str',
              'record|0-1': 0,
              'series|1-3': [{
                'id|+1': 1,
                'type|1-2': 2,
                'name|1-2': '小云熊奇妙故事',
                'mark': null,
                'status': 0
              }]
            }]
          }
        });
        res.json(data);
      } else {
        next();
      }
    })
  }
}

module.exports = new MockHandler(config);
复制代码

设置了两个并联开关,分别设置环境变量mock或者往请求查询参数加入_is_mock变量均可以经过验证。

逻辑与配置分离:

const config = {
    '/api/action/game/list': {
      code: 0,
      data: {
        'totalCount|1-100': 1,
        'datas|1-10': [{
          'id|+1': 1,
          'dataName|1-5': 'name',
          'dataType|1-2': 1,
          'dataScope': '1,100',
          'defaultValue|1-5': 'value',
          'dataStatus|0-1': 0,
          'remark|1-5': 'str',
          'record|0-1': 0,
          'series|1-3': [{
            'id|+1': 1,
            'type|1-2': 2,
            'name|1-2': '小云熊奇妙故事',
            'mark': null,
            'status': 0
          }]
        }]
      }
    }
}

class MockHandler {
  constructor(config) {
      this.config = config
  }
  
  handle(app, server) {
    Object.keys(this.config).forEach(route => {
        app.get(route, function (req, res, next) {
            if (!!process.env.mock || !!req.query._is_mock) {
                var data = Mock.mock({
                  code: 0,
                  data: {
                    'totalCount|1-100': 1,
                    'datas|1-10': [{
                      'id|+1': 1,
                      'dataName|1-5': 'name',
                      'dataType|1-2': 1,
                      'dataScope': '1,100',
                      'defaultValue|1-5': 'value',
                      'dataStatus|0-1': 0,
                      'remark|1-5': 'str',
                      'record|0-1': 0,
                      'series|1-3': [{
                        'id|+1': 1,
                        'type|1-2': 2,
                        'name|1-2': '小云熊奇妙故事',
                        'mark': null,
                        'status': 0
                      }]
                    }]
                  }
                })
                res.json(data);
            } else {
                next();
            }
        })
    })
  }
}
复制代码

革命还没有成功,旧的问题的解决了,但同时又出现的新的问题:

  • 只支持get请求,不支持post请求
  • get方法的回调函数中重复了不少次对mock开关条件的判断,须要提取出来

针对第一个问题,只须要将get/post请求方法配置到route就行了,自己属于配置逻辑的范围以内。而第二个问题,能够提炼函数,利用函数式编程,利用高阶函数返回回调函数,这样代码会简洁不少,具体优化以下:

const config = {
    'GET /api/action/game/list': {
      code: 0,
      data: {
        'totalCount|1-100': 1,
        'datas|1-10': [{
          'id|+1': 1,
          'dataName|1-5': 'name',
          'dataType|1-2': 1,
          'dataScope': '1,100',
          'defaultValue|1-5': 'value',
          'dataStatus|0-1': 0,
          'remark|1-5': 'str',
          'record|0-1': 0,
          'series|1-3': [{
            'id|+1': 1,
            'type|1-2': 2,
            'name|1-2': '小云熊奇妙故事',
            'mark': null,
            'status': 0
          }]
        }]
      }
    }
}

class MockHandler {
  constructor(config) {
    this.init(config);

    this.config = config;
  }
  init(config) {
    Object.keys(config).forEach(route => {
      const model = config[route];

      function callbackWarpper(model) {
        return function (req, res, next) {
          if (isMock || !!req.query._is_mock) {
            var data = Mock.mock({
              code: 0,
              data: model
            });
            res.json(data);
          } else {
            next();
          }
        };
      }
      config[route] = callbackWarpper(model);
    });
  }
  handle(app, server) {
    for (let route in this.config) {
      let [method, path] = route.split(' ')
      let reg = new RegExp(path);
      let callback = this.config[route];
      app[method.toLowerCase()](reg, callback);
    }
  }
}
复制代码

写到这里本觉得本身写出了一手漂亮的代码,可是忽然想起,app实例上有个use()能够用来忽略请求方法get/post,而后在内部对请求作匹配处理,这样代码会更加的精简和容易被理解:

const isMock = !!process.env.mock;
const config = {
    '/api/action/game/list': {
      code: 0,
      data: {
        'totalCount|1-100': 1,
        'datas|1-10': [{
          'id|+1': 1,
          'dataName|1-5': 'name',
          'dataType|1-2': 1,
          'dataScope': '1,100',
          'defaultValue|1-5': 'value',
          'dataStatus|0-1': 0,
          'remark|1-5': 'str',
          'record|0-1': 0,
          'series|1-3': [{
            'id|+1': 1,
            'type|1-2': 2,
            'name|1-2': '小云熊奇妙故事',
            'mark': null,
            'status': 0
          }]
        }]
      }
    }
}

class MockHandler {
  constructor(config) {
    this.config = config;
  }
  handle(app, server) {
    app.use((req, res, next) => {
      if (isMock || !!req.query._is_mock) {
        Object.keys(this.config).forEach(route => {
          const reg = new RegExp(route)
          const model = this.config[route]
          if (reg.test(req.path)) {
            var data = Mock.mock({
              code: 0,
              data: model
            });
            return res.json(data);
          }
        })
        next()
      } else {
        next()
      }
    })
  }
}
复制代码

这个方案原理是利用webpack devServer在发送请求时候作了一层拦截,请求并无通过真实的网络环境,可是利用webpack devServer其实有个弊端,当我想增长一条记录时,须要再次重启webpack,自己属于对webpack配置的修改,除利用node watch功能监听文件改动运行脚本重启的方法外,几乎想不出有更好的方法了,这个痛点用一个名词来形容就是不支持热插拔。

浏览器插件

有没有不涉及到编程的mock方案呢?有,利用浏览器插件拦截请求或篡改响应。

这种方案属于利用工具,不用在代码层面引入mock层也能够拦击请求,好处是相对前面介绍的方案,更加的具备普适性,可让测试小伙伴在后端接口发生异常的状况下依然畅通无阻的测试前端功能。

这样的插件我推荐: Ajax Interceptor 下载 源代码

工具

不想用浏览器插件还有其余的工具吗?固然有,就是咱们熟悉的postman,详细请参考下面文章:

Postman高级应用(11):能够开始对接了吗——Mock服务

相关文章
相关标签/搜索