概念
Protocol Buffers (以下称 PB)是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 不得不说,这个概念看起来就让人一头雾水。其实我们可以通俗的理解为 PB 就是一个类似于 JSON 的数据交换格式,那不同的是 JSON 是文本格式,方便直接阅读,但 PB 是二进制格式,不利于直接阅读。
为什么要使用 PB?
-
跨语言性,跨平台性
PB 支持
Java、C++、Python等多种语言,支持多个平台。前后端文档只需要定义一份描述文件即可协作。 -
体积小
PB 对数据序列化后,体积小近 3 倍。
-
高效
二进制格式优势
前端需不需要使用 PB?
首先我们来看看前端使用 JSON 的好处:
- JS 原生支持,符合 JS 的原生语法,编写阅读成本低。
- 解析方便,使用 JSON.parse() 和 JSON.stringify() 即可,不需要第三方库支持。
- 相较于 PB 不需要描述文件,直接定义传输数据,学习成本低。
- 抓包方便,通过Chrome DevTools等抓包工具抓取 JSON 数据后可以直接查看。
综上所述,JSON 对于 JS 来说是非常契合的数据交换格式,而 PB 需要引入第三方包进行解析,还不利于抓包阅读调试,在什么情况下我们需要使用 PB 呢?
其实主要是单个请求数据比较大的时候,例如几 M 的数据包时,前端主要处理手段是分包拉取,这样的话无论是并发加载还是阻塞加载都会增加代码的复杂性,而且一定程度后都会出现不同的性能瓶颈和请求时间瓶颈。 这个时候就是使用 PB 这种数据交换格式的时候了,相同的数据内容下,大约4.3 M的 PB文件等价于16M 的 JSON 文件。
PB 在前端的使用
PB 在前端中的使用主要分三个阶段:
- 编写描述文件,类似于 TS 中申明数据类型
- 加载描述文件,编写相应的数据内容
- 编译成二进制与反编译
首先npm安装 protobufjs:
npm install protobufjs --save
编写描述文件.proto
// school.classmate.proto, 一般命名规则 ${package_name}.${message_name}.proto
// 自定义 package 名
package school;
// 如果不声明 syntax = "proto3";,则默认使用 proto2 进行解析
syntax = "proto3";
// pb 中的消息载体都被称为 message
// 自定义 message 名
// 定义该 message 中有几个成员,分别的数据类型,是否必要
// 每个消息定义中的每个字段都有唯一的编号。这些字段编号用于标识消息二进制格式中的字段,并且在使用消息类型后不应更改。
// 范围 1 到 15 中的字段编号需要一个字节进行编码,一般用于频繁出现消息
message classmate {
required int32 id = 1;
required string name = 2;
optional int32 sex = 3;
}
编写消息主体文件
// 引入三方库
var Protobuf = require("protobufjs");
// 加载描述文件
Protobuf.load("school.classmate.proto")
.then(function(root) {
var Message = root.lookupType("school.classmate");
// 定义消息
var info = {
id: 1,
name: "Jack"
};
// 验证是否符合规则
var errMsg = Message.verify(info);
if(errMsg)
throw Error(errMsg);
var classmateInfo = Message.create(info);
// classmate { id: 1, name: 'Jack' }
var buffer = Message.encode(classmateInfo).finish();
// <Buffer 08 01 12 04 4a 61 63 6b>
var decodeMessage = Message.decode(buffer);
// classmate { id: 1, name: 'Jack' }
})