2022年11月6日
Vue优化–json-serve与mock.js本地接口调试
背景:在项目开发中,常常需要边写前端页面边写后端接口,但是后端接口服务往往是滞后于前端开发的,或者是不能及时提供的。出于前端开发的迅速和便捷去考虑,我们可以根据后端接口数据结构去模拟(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