Protobuf是Google开源的一种可用于结构化数据串行化(序列化)的数据缓存格式,适用于数据存储以及RPC数据交换格式.php
在前端使用时,会经过自动转换器将proto文件转换为对应的js文件,进行下一步使用。 转换方式有两种:前端
proto文件通过转换后,会生成一个protoFileName.js文件,文件中会包含一系列读取、设置字段的方法,至关于第一个字段成为私有字段,同时提供Public的get和set方法,对字段进行处理。这样的好处是,字段被保护起来,减小出错,相对安全。java
以下一个proto结构定义,表现的信息git
message Person {
uint32 id = 1;
string first_name = 2;
string last_name = 3;
Email email = 4;
BirthdayMonth birthday_month = 5;
bool active = 6;
}
复制代码
通过转换后,(以转化为js为例),成为一个类的实例,github
至关于缓存
info = new Person()
info.setFisrtName('YOUR FIRST NAME')
info.setLastName('YOUR LAST NAME')
复制代码
此实例的属性信息以下, 能够看到它的原型是jspb.Message, (一个抽象公共类)安全
array: (5) [100, "YOUR FIRST NAME", "YOUR LAST NAME", Array(2), 1]
arrayIndexOffset_: -1
convertedFloatingPointFields_: {}
messageId_: undefined
pivot_:1.7976931348623157e+308
wrappers_: {4: proto.Email}
__proto__: jspb.Message
复制代码
其中:bash
它的原型方法有哪些呢?数据结构
Object.keys(info.__proto)
// 输出 ["constructor", "toObject", "serializeBinary", "getId", "setId", "getFirstName", "setFirstName", "getLastName", "setLastName", "getEmail", "setEmail", "clearEmail", "hasEmail", "getBirthdayMonth", "setBirthdayMonth", "getActive", "setActive"]
复制代码
能够看到, 除去前两个方法是Object类型共有,后面的方法都是根据字段自动生成的,其中,若是字段是复合字段,(即message类型),这个字段还会额外增长clearEmail(就是置空)方法。app
代码示例以下:
proto.Person.prototype.getId = function() {
return jspb.Message.getFieldWithDefault(this, 1, 0);
};
proto.Person.prototype.setId = function(value) {
jspb.Message.setProto3IntField(this, 1, value);
};
proto.Person.prototype.getFirstName = function() {
return jspb.Message.getFieldWithDefault(this, 2, "");
};
proto.Person.prototype.setFirstName = function(value) {
jspb.Message.setProto3StringField(this, 2, value);
};
proto.Person.prototype.getLastName = function() {
return jspb.Message.getFieldWithDefault(this, 3, "");
};
proto.Person.prototype.setLastName = function(value) {
jspb.Message.setProto3StringField(this, 3, value);
};
proto.Person.prototype.getEmail = function() {
return jspb.Message.getWrapperField(this, proto.Email, 4);
};
proto.Person.prototype.setEmail = function(value) {
jspb.Message.setWrapperField(this, 4, value);
};
proto.Person.prototype.clearEmail = function() {
this.setEmail(undefined);
};
proto.Person.prototype.hasEmail = function() {
return jspb.Message.getField(this, 4) != null;
};
proto.Person.prototype.getBirthdayMonth = function() {
return jspb.Message.getFieldWithDefault(this, 5, 0);
};
proto.Person.prototype.setBirthdayMonth = function(value) {
jspb.Message.setProto3EnumField(this, 5, value);
};
proto.Person.prototype.getActive = function() {
return jspb.Message.getFieldWithDefault(this, 6, false);
};
proto.Person.prototype.setActive = function(value) {
jspb.Message.setProto3BooleanField(this, 6, value);
};
复制代码
每一种数据类型都有对应的赋值方法,最终都会调用同一个方法setFieldIgnoringDefault_
jspb.Message.setProto3IntField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0);
};
jspb.Message.setProto3FloatField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0.0);
};
jspb.Message.setProto3BooleanField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, false);
};
jspb.Message.setProto3StringField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, "");
};
jspb.Message.setProto3BytesField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, "");
};
jspb.Message.setProto3EnumField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0);
};
复制代码
粗略看过去,方法不少,关键参数是其中的1-6的数字,这就是proto定义中的数字,在js实例中,array字段存储了全部属性信息,若索引和上述数字是一一对应的,固然,若是字段为空,在array中会skip.
好比, proto中定义了10个字段,其中只有3,6字段不为空,则最终数据结构为
array(empty, emtpy, 3, empty, empty, 6)
复制代码
这个转换过程当中,有些细节须要关注。
第一个问题, 如何将proto转为js
protobuf 提供的compile, 有相应的php 、js 、Python, java版本, 见github.com/protocolbuf…
第二个问题, 实际的转化代码
以js为例, 用c写的转换器,
第三个问题,jspb 从何而来
protobuf/js下binary文件夹及message.js中定义了jspb相关的方法,在转换过程当中,会自动集成在一块儿
一些总结:
好处的话很少说,自动转化,封装性
还可优化的地方,方法定义太多,致使类似代码太多冗余,还能够再精简一下,好比get, set方法再进行抽象下,将字段当成参数,不用一必定义,相似php中的__call方法。