2022年11月6日

Vue优化–json-serve与mock.js本地接口调试

作者 admin

背景:在项目开发中,常常需要边写前端页面边写后端接口,但是后端接口服务往往是滞后于前端开发的,或者是不能及时提供的。出于前端开发的迅速和便捷去考虑,我们可以根据后端接口数据结构去模拟(mock)数据从而实现前端的独立开发。
JsonServer 主要的作用就是搭建本地的数据接口,创建json文件,便于调试调用
Mockjs 主要的作用就是生成随机数据,支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等

技术:vuecli4,vue3.3,ts环境

如何使用:

文档:https://github.com/nuysoft/Mock/wiki

代码:https://github.com/nuysoft/Mock/tree/refactoring

实例:http://mockjs.com/examples.html

实际项目中使用:

开始 & 安装

npm install mockjs

// 使用 Mock
var Mock = require('mockjs')
var data = Mock.mock({
    // 属性 list 的值是一个数组,其中含有 1 到 10 个元素
    'list|1-10': [{
        // 属性 id 是一个自增数,起始值为 1,每次增 1
        'id|+1': 1
    }]
})
// 输出结果
console.log(JSON.stringify(data, null, 4))

//data.js如下图
data.js

// 引入mock.js
let Mock = require('mockjs')
// import Mock from 'mock'
let Random = Mock.Random;

// 编写一个函数,这个函数利用mock.js动态生成mock数据
// 我看到很多教程这里是直接输出一个对象的,这样每次访问得到的都是同样的数据。不符合预期,
// 我们这里用函数包装,每次访问api的时候就执行一次该函数,得到的就是新的mock数据了
function testList() {
  const monitorList = [];
  for (var i = 0; i < 3; i++) {
    monitorList.push(
      Mock.mock({
        userid: '25788869998_1562054852076593528',
        appid: Random.natural(100, 3000),
        content: Random.cparagraph(2, 4),
        timestamp: 1562054852,
      })
    );
  }
  
  // 这里返回你想要的数据格式
  return {
    success: true,
    data: monitorList,
    message: '获取数据成功',
  };
}

// 以api路径为值,将testList函数暴露出去
exports.user_info = testList;

db.js

db.js
 // 引入mockDB文件夹下的文件,假如有以下文件
 let test1 = require('./data.js');
//  let test2 = require('./mockDB/test2.js');
console.log('test1', test1);
 
 // 这里定义一个收集的对象
let dbJson = {
  func: {},
  json: {},
}
// 收集mock函数,在server.js会用到
dbJson.func = {
  ...test1,
  // ...test2,
};

console.log('测试dbJson.func',  dbJson.func);

// 获取所有的api路径
let apiPath = Object.keys(dbJson.func);

// 组装json-server需要的路由表,这里为什么都是空对象呢?
// 上面说过,路由表的value必须是对象或者数组。但我们的mockDB文件夹下定义的都是mock函数,所以这里要保证路由表是正确的,就要重新组装一份符合格式的路由表。
apiPath.forEach(item => {
  dbJson.json[item] = {"aaa":"bbb"};
});

exports.dbJson = dbJson.json;
exports.dbFunc = dbJson.func;

index.js 启动入口

// index.js
const path = require('path');
const fs = require('fs');
const jsonServer = require('json-server');
const mockJs = require('mockjs');
const Random = mockJs.Random;
const customMiddleware = require('./middleware');
const glob = require('glob');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();

// 扩展mock自定义规则
mockJs.Random.extend({
    richText: function(date) {
        var constellations = ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座']
        return this.pick(constellations)
    }
})
// mock数据,常驻内存
let data = {}
/**
 * mock解析json文件
 * @param file
 * @returns {*}
 */
function parsingToMockJs(file) {
    const json = fs.readFileSync(file, 'utf-8');
    return mockJs.mock(JSON.parse(json));
}

/**
 * 合并json数据
 * @param path
 */
function mergeJsonData(path) {
    Object.assign(data, parsingToMockJs(path));
}

/**
 * 扫描mock目录,生成mock数据
 */
 glob(path.join(__dirname, `/mockData/*.json`), {}, (err, files) => {
  files.forEach(item => {
      mergeJsonData(item);
  });
 
  const router = jsonServer.router(data);

  // 自定义路由
  server.post('/api/upload/image', (req, res) => {
      res.jsonp({
          id: Random.image('300x250', Random.color())
      });
  })

  server.use(jsonServer.bodyParser);
  server.use(middlewares);
  // 添加响应头
  server.use((req, res, next) => {
      res.header('author', 'Ice');
      next();
  });

  server.use(customMiddleware);

  // 数据统一封装
  router.render = (req, res) => {
      res.jsonp({
          success: true,
          code: 0,
          msg: 'success',
          data: res.locals.data
      });
  }
  server.use('/api', router);
  server.listen(3000, () => {
      console.log('Mock server is running......')
  });
});



middleware.js请求处理
const mockJs = require('mockjs')
const Random = mockJs.Random

module.exports = (req,res,next)=>{
    // 可以对请求进行特殊操作
    console.log(req.body);
    console.log(req._parsedUrl);
    if(req.method === 'POST' && req._parsedUrl.pathname === '/api/dynamics') {
      req.body.imageUrl = Random.image('300x250', Random.color());
      req.body.viewCount = Random.natural(1, 1000);
    }
  
    next()
}
mock.js主入口
const jsonServer = require('json-server');
// 创建json-server实例
const server = jsonServer.create();
const jsonDB = require('./db');
// 将路由配置表传入,生成路由表
const router = jsonServer.router(jsonDB.dbJson);
const middlewares = jsonServer.defaults();

// 这里比较重要。
// 目的是:重新定义路由返回的数据。就是说:每访问一个路由,返回response的时候,都可以经过router.render重新定义response数据
// 因为我们在db.js里将mock函数收集到了dbJson.func对象里。
router.render = (req, res) => {
    // 根据请求的url,截取路径。
    // 例如:一般情况,res.url是/user_info?id=1234这样的格式。
    let end = req.url.indexOf('?');
    let apiName = req.url.slice(1, end) || '';
    // 根据apiName拿到对应的mock函数,然后返回mock函数生产的mock数据
    // 因为每次返回response的时候,都会执行一次该函数,所以得到的都是新的mock数据
    let response = jsonDB.dbFunc[apiName] && jsonDB.dbFunc[apiName]();
    res.send(response || {});
};

server.use(middlewares);
// 导入路由
server.use(router);
// 在6666端口开服务
server.listen(3000, () => {
  console.log('JSON Server is running');
});

然后开始模拟数据,看下自己创建的模拟文件

task.json

{
  "user-infoHealthTask|1": [
    {
      "task_id": "@integer(1,100)",
      "create_time": "@date",
      "update_time": "@date",
      "task_type": "@title",
      "task_type_child": "体育课",
      "state|1": ["已完成","未完成","已点评","待完成"],
      "patient_id": "AA00000003",
      "task_time": "@date",
      "task_optometrist_id": 22,
      "task_optometrist_name": "@ctitle",
      "weight": 2,
      "cycle": "@ctitle",
      "cycle_id": 0,
      "memo": "",
      "content": "\u003cp\u003e让孩子学习下这个视频:\u003c/p\u003e\u003cdiv data-w-e-type=\"video\" data-w-e-is-void\u003e\n\u003cvideo poster=\"\" controls=\"true\" width=\"auto\" height=\"auto\"\u003e\u003csource src=\"https://bolin-digital-therapy.oss-cn-beijing.aliyuncs.com/archive%2F166375006668955683b493236d4.mp4?Expires=4817350062\u0026OSSAccessKeyId=LTAI6wrvk6bAKfzk\u0026Signature=xuZd4mRDT8pkwdhznEJM0vzb3JA%3D\" type=\"video/mp4\"/\u003e\u003c/video\u003e\n\u003c/div\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e",
      "submit_items": "[{\"name\":\"左眼\",\"desc\":\"检查视力\",\"type\":\"NUM\",\"required\":true,\"user_name\":\"测试视频\",\"user_desc\":\"是的VG导入法\",\"user_type\":\"IMG\",\"value\":\"2.0\"},{\"required\":true,\"name\":\"双眼\",\"desc\":\"检查下视力\",\"type\":\"NUM\",\"user_radio\":\"0\",\"user_name\":\"散热\",\"user_desc\":\"它包含任何\",\"user_type\":\"NUM\",\"value\":\"3.0\"},{\"name\":\"右眼\",\"desc\":\"检查视力\",\"type\":\"NUM\",\"required\":true,\"user_radio\":\"0\",\"user_name\":\"儿童和人\",\"user_desc\":\"儿童和人\",\"user_type\":\"TEXT\",\"value\":\"4.0\"},{\"required\":true,\"name\":\"如图一技能\",\"desc\":\"容统一缴纳\",\"type\":\"IMG\",\"value\":[\"http://bolin-digital-therapy.oss-cn-beijing.aliyuncs.com/archive%2F03EF4748-85FE-466C-B56A-BBD64D50C71F.jpeg?Expires=4818120907\u0026OSSAccessKeyId=LTAI6wrvk6bAKfzk\u0026Signature=SPuhXUzqzgv%2FsJEGqAJKSxRriU8%3D\"]}]",
      "submit_finish_time": "@date",
      "task_comment": "@richText",
      "task_comment_time": "@date",
      "task_comment_optometrist_name": "",
      "sent_state": 1,
      "name": "@name",
      "sex": "男",
      "age": "@integer(1,100)",
      "birthday": "@date",
      "optometrist": 16,
      "mobile": "15622333311",
      "view_type": "",
      "view_name": "",
      "is_read": "@integer(0,2)"
  }
  ]
}

解释:比如”task_id”: “@integer(1,100)”

key是json-server的字段,value@integer(1,100) 是指随机返回一个1-100的数字

运行前改一下package.json加入

"scripts": {

    "mock": "set VUE_APP_MOCK=true& set VUE_APP_MOCK_BASE_API=/mock/126/ajax&& vue-cli-service serve",
    "mock:serve": "set VUE_APP_MOCK=true& set VUE_APP_MOCK_BASE_API=http://localhost:3000& vue-cli-service serve& nodemon ./src/mock/index.js",
    "mock:ser": "cross-env VUE_APP_MOCK=true VUE_APP_MOCK_BASE_API=http://localhost:3000 vue-cli-service serve",
    "mock:json": "nodemon ./src/mock/index.js"
  },

ok,齐活了 ,开启一个新的终端切换到项目的目录下 执行命令 就行了 。

其它参考文章:https://www.jianshu.com/p/72e404c9be2e

http://t.zoukankan.com/ranyonsue-p-13189316.html