前端如何封装protobuf进行数据交互,我已经在这里面写清楚了,可以先看这篇文章.更容易懂我本篇文章
前端protobuf请求响应封装(axios)
但是在开发过程中,会发现一个问题,比如在使用后端给我们的proto的文件的时候,其实很多接口有可能是废弃的,或者说是其他活动的, 枚举是多余的,等等,总之会出现这种问题.
对于我们用不上的proto,我们依然通过指令将所有的proto文件转化成了json,实际上我们只需要保留我们的api用到了的接口,所以存在一个json文件的压缩操作.
1.场景问题
我拿时间戳接口,奖励记录接口进行测试
以下是这两个接口完整的proto文件:
通过protobufjs-cli进行转化成json
"scripts": {
"proto": "npx pbjs -t json src/protobuf/protoFile/*.proto > src/protobuf/protoFile/proto.json",
},
这时候proto.json的占用大小为3.21kb
引入proto.json之后,我们能到拿到这两个接口的数据
假设我这个活动不需要调用时间戳了,只需要调用奖励记录接口,那么我们应该根据我们需要的api,得出最简化的json,再引入,肯定不可能手动去剔除不要的proto文件或者里面的内容,因为一个package里面包含很多的接口
2.实现过程
2.1 配置运行指令
"scripts": {
"compressProto": "node src/protobuf/compressProto/index"
},
2.2 1 下载包
npm i protobufjs-cli colors --save
2.3 代码
const colors = require("colors");
const path = require("path")
// 1.首先应该对pb进行压缩-开启子进程
const { execSync } = require('child_process')
execSync(`npx pbjs -t json src/protobuf/protoFile/*.proto > src/protobuf/protoFile/proto.json`, {
encoding: "utf8",
})
// 2.拿到所有的pb转化的json数据
const allProtoJson = require('../protoFile/proto.json')
// 3. 拿配置的接口与全部的json进行匹配,主要是根据名字来匹配,只保留当前的需要的,进行压缩
const projectCurrentApiJson = require('./api/conf.json').api
const commonPath = path.resolve(__dirname, '../');
const { minifyPb } = require("./minify");
minifyPb(
projectCurrentApiJson,
allProtoJson,
`${commonPath}/protoFile/index.min.json`
)
console.log(colors.green("pb压缩成功, 生成index.min.json"));
其实要理解的是转化之后json的结构是什么样,除了固定的api名+req和api+res,其他的非基础类型的对象和他们都是同级的,所以逻辑是:
1.先找到allProtoJson里面所有关于接口请求有关的key-requestResponseKeys这里我们用xxx进行匹配.就能拿到allProtoJson所有的api对应的req名字和res名字.
2.通过const methodsInfo = nested[ajaxKey]["methods"][subMethods.name],也就是nested底下一个个去判断requestResponseKeys里面的元素底下的req和res是否存在,如果存在就是表示这个api是被需要的,并且赋值给新创建的newNested
newNested[ajaxKey] = newNested[ajaxKey] || { methods: {} };
newNested[ajaxKey]["methods"][subMethods.name] = methodsInfo;
3.用types来存newNested所有的api名+req和api+res的同级key,目前只拿到了接口的res和req
const { requestType, responseType } = methodsInfo
const ajaxArr = [requestType, responseType]
// 2.1 types里面加入item
ajaxArr.forEach(item => {
if(!types.includes(item)) {
types.push(item)
}
})
4.addTypesToNested用来将api的req和res里面的非基础类型的添加到json上
function addTypesToNested(nested, newNested, types) {
// 1.得到的types应该还要加上服务端统一的请求
types = types.concat(commonTypes);
for (let i = 0; i < types.length; i++) {
// 2.如果nested[types[i]]存在但是newNested[types[i]不存在,就进行赋值
if (!newNested[types[i]] && nested[types[i]]) {
newNested[types[i]] = nested[types[i]];
// 3.判断fileds里面是不是基础类型,如果是,就不用管,不是就作为types中的 Enum类型没有fileds
let fields = nested[types[i]].fields || {};
Object.keys(fields).forEach((key) => {
// 如果不是基本类型,就是可以需要被单独弄成属性的, types要新增
if (!primitiveTypes.includes(fields[key].type)) {
types.push(fields[key].type); // 新增后,引用类型可以新增循环
}
});
}
}
}
代码如下: conf.json
{
"api": [
{
"remark": "活动奖励明细",
"name": "RechargeBonusList"
}
]
}
const fs = require("fs")
// 原始类型 不需要递归(不需要作为key)
const primitiveTypes = [
"int32",
"int64",
"sint32",
"sint64",
"uint32",
"uint64",
"bytes",
"string",
"bool",
];
// 保存压缩后的代码
function saveMiniJson(data, filepath) {
const str = JSON.stringify(data,"","\t") // 格式化成字符串
fs.writeFile(filepath, str, { 'flag': 'a' }, function(err) {
if (err) {
throw err;
}
// 写入成功后读取测试
fs.readFile(filepath, 'utf-8', function(err, data) {
if (err) {
throw err;
}
console.log(`index.min.json已经写入啦`)
});
});
}
/**
* 给nested添加方法
* @param {*} nested
* @param {*} newNested
* @param {Array} methods
* @return [types] 方法用到的类型
*/
function addFunctionToNested(nested, newNested, methods) {
// 方法需要的types,也就是对应的json里面package的nest底下的key,
//首先需要确定的是所需要的方法是从方法名从而拿到请求
// 和相应需要的所有的type,而含属于请求的可以通过固定的ExtObj进行匹配到
// 1.拿到与请求响应有关的key [ 'ActivityExtObj', 'IndexExtObj' ]
const requestResponseKeys = Object.keys(nested).filter((key) => {
return /xxx$/i.test(key)
})
// 2.api中含有的请求响应的key, methods是一定在requestResponseKeys里面的,
挑出methodsInfo存在的数据
const types = [] // [ 'GetTimestampReq', 'GetTimestampRes' ]
requestResponseKeys.forEach(ajaxKey => {
methods.forEach(subMethods => {
const methodsInfo = nested[ajaxKey]["methods"][subMethods.name]
// 如果存在数据
if (methodsInfo) {
const { requestType, responseType } = methodsInfo
const ajaxArr = [requestType, responseType]
// 2.1 types里面加入item
ajaxArr.forEach(item => {
if(!types.includes(item)) {
types.push(item)
}
})
// 2.2存在的就给newNested加上
// 给新的newNested添加上, 存在的api上的方法
newNested[ajaxKey] = newNested[ajaxKey] || { methods: {} };
newNested[ajaxKey]["methods"][subMethods.name] = methodsInfo;
}
})
})
return types;
}
const commonTypes = ["xxxOutxxx", "xxxInxxx"];
// 添加消息体
function addTypesToNested(nested, newNested, types) {
// 1.得到的types应该还要加上服务端统一的请求
types = types.concat(commonTypes);
for (let i = 0; i < types.length; i++) {
// 2.如果nested[types[i]]存在但是newNested[types[i]不存在,就进行赋值
if (!newNested[types[i]] && nested[types[i]]) {
newNested[types[i]] = nested[types[i]];
// 3.判断fileds里面是不是基础类型,如果是,就不用管,不是就作为types中的 Enum类型没有fileds
let fields = nested[types[i]].fields || {};
Object.keys(fields).forEach((key) => {
// 如果不是基本类型,就是可以需要被单独弄成属性的, types要新增
if (!primitiveTypes.includes(fields[key].type)) {
types.push(fields[key].type); // 新增后,引用类型可以新增循环
}
});
}
}
}
/**
* 打包压缩pb
* @param {*} methods 所需方法配置
* @param {*} pbJson 原有pb
* @param {*} savePath 保存路径
*/
function minifyPb(methods, pbJson, savePath) {
const formatMethods = methods
// 1.拿到所有的package ,也就是json的第一层key .类似于pb, gm_pb等
const pbs = Object.keys(pbJson.nested); // [ 'pb' ] 拿到所有的packages
// 2.创建空json对象来创建
let newNested = {};
pbs.forEach((pb) => {
// 2.1 为每个package创建新的对象
newNested[pb] = { nested: {} };
const nested = pbJson.nested[pb].nested; // 每个package底下所有的用到的方法
// 2.2 为每个package添加必要的方法 [ 'GetTimestampReq', 'GetTimestampRes' ]
let needTypes = addFunctionToNested(
nested,
newNested[pb].nested,
formatMethods
);
// 添加消息体
addTypesToNested(nested, newNested[pb].nested, needTypes);
});
// 保存JSON
saveMiniJson(Object.assign(pbJson, { nested: newNested }), savePath)
}
module.exports = {
minifyPb,
};
3结果
index.min.json中我们将10项压缩成了6项,大小也变了
也可以直接压缩成无json格式字符串:
// const str = JSON.stringify(data,"","\t") // 格式化成字符串
const str = JSON.stringify(data) // 格式化成字符串
修改上篇文章引入方式:
import protoJson from '../protoFile/index.min.json'