自动生成proto Js语句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自动生成proto Js语句相关的知识,希望对你有一定的参考价值。

在与后端的WebSocket通信时,前端要带一个proto文件是一个累赘的事情。首先是明显的曝光了协议实体对象,再一个浏览器客户端很容易会缓存该文件,新的协议更新可能导致客户端不能使用,另外在cdn服务器上还需要配置.proto类型客户端才能下载过去。真是遗毒不浅,自己使用的时候会注意这些,但给别人使用的时候就很不乐观了,所以这次全部将proto文件转成javascript对象,省去协议文件和加载的步骤。

先看代码:

 function createProto(name) {
        var args = [].slice.call(arguments, 1);
        var obj = new protobuf.Type(name);
        for (var i = 0; i < args.length; i++) {
            var p = args[i];
            var key = i + 1;
            obj.add(new protobuf.Field(p[0], key, p[1] || "string"));
        }
        return obj;
    }

    function createEnum(name,list) {
        var obj = new protobuf.Enum(name);
        for (var i = 0; i < list.length; i++) {
            obj.add(list[i],i);
        }
        return obj;
    }

    function loadProto(callback) {
         if (typeof protobuf == "undefined") return;//说明浏览器加载失败            root = new protobuf.Root().define("IMEntity");
            root.add(createProto("Token", ["UserID"], ["Token"], ["Device"], ["Version", "int32"], ["Appkey"]));
            root.add(createProto("Feedback", ["ResultCode", "int32"], ["ResultData"], ["Seq", "int32"], ["MsgID"]));
            root.add(createEnum("ReceiptType", ["Receive", "Read"]));
       //...
util.triggerCallback(callback); };

proto 主要有两种类型,Type和Enum。Type对应协议中的message,相当于是类。Enum就是枚举类型

var Root  = protobuf.Root,
    Type  = protobuf.Type,
    Field = protobuf.Field;

var AwesomeMessage = new Type("AwesomeMessage").add(new Field("awesomeField", 1, "string"));

var root = new Root().define("awesomepackage").add(AwesomeMessage);

枚举的创建不要需要Field。只需要add 字段名即可。那么接下来的问题是,手写root.add 也很烦,因为要一个一个对照属性,不断的复制粘贴,很容易出错。所以又做了个自动生成代码的页面:

 <textarea id="content">
        //登陆Token
        message Token{
        string UserID = 1;    //登陆接口返回的IMUserID
        string Token = 2;    //登陆接口返回的Token
        string Device = 3;    //客户端设备号
        int32  Version = 4;    //版本号,发布前与服务端约定值
        string Appkey = 5;    //客户端Appkey
        }
       
        //收到私信
        message ReceivePrivateMessage{
        string MsgID = 1;        //消息id
        string SenderID = 2;    //发送者id
        string ReceiverID = 3;    //接收者id
        string Content  = 4;    //消息内容。客户端转换成业务相关的实体后,再做后续处理(客户端使用,服务器不做任何处理,原样下发)
        bool Ack = 5;              //是否需要已读回执
        int32 SendDateTime = 6;    //消息发送时间
        int32 ContentType = 7;    //内容类型(客户端使用,服务器不做任何处理,原样下发)
        }
        //回执类型
        enum ReceiptType{
        Receive = 0;                      //已收回执(收到消息后立即发送给服务器的回执)
        Read = 1;                        //已读回执(用户进入消息阅读界面后发送给服务器的回执)
        }
    </textarea>
    <div id="result"></div>
    <script>
        function start() {
            $("#result").html("");
            $("#result").append(\'root = new protobuf.Root().define("IMProtoEntity")<br>\');


            var reg = /("([^\\\\\\"]*(\\\\.)?)*")|(\'([^\\\\\\\']*(\\\\.)?)*\')|(\\/{2,}.*?(\\r|\\n))|(\\/\\*(\\n|.)*?\\*\\/)/g,// 过滤注释
          str = $(\'#content\').val(); // 欲处理的文本
            // console.log(str.match(reg));// 打印出:匹配子串
            var news = str.replace(reg, "");
            // console.log(news); // 打印出:原文本
            var reg1 = /[message|enum].*?{/mg;
            var regobj = /{[^}{]*?}/g;//新地址
            var names = news.match(reg1);
            var protos = news.match(regobj);
            // console.log(names, protos);
            var root = {};
            for (var i = 0; i < names.length; i++) {
                var rawname = names[i];
                var rawObj = protos[i];
                //if (~rawname.indexOf("message"))
                if (!rawObj) continue;

                var name = rawname.replace("{", \'\').replace("message ", \'\').replace("enum ", \'\');
                var obj = { name: name };
                if (~rawname.indexOf("enum")) {
                    obj["type"] = "enum";
                }

                rawObj = rawObj.replace("{", \'\').replace("}", \'\');
                var protolist = rawObj.split(\';\');
                //  console.log("protolist", protolist);
                var plist = [];
                for (var j = 0; j < protolist.length; j++) {
                    var p = $.trim(protolist[j]);
                    if (p) {
                        var args = [];
                        var list = p.split(\' \');
                        //  console.log("list", list);
                        list.forEach(function (n) {
                            n && args.push(n);
                        }),
                        //  console.log("args", args);
                        plist.push(args);
                    }
                }
                obj.list = plist;
                console.log(obj);
                toProto(obj);
            }

        }

        start();

        function toProto(obj) {
            var root = "root";
            var fun = "createProto";
            var enumfun = "createEnum";

            var str = root + \'.add(\';
            var args;
            if (!obj.type) {//message
                args = \'\';
                for (var i = 0; i < obj.list.length; i++) {
                    var item = obj.list[i];

                    //老协议2.0
                    if (item[0] == "required" || item[0] == "optional") {
                        item.shift();
                    }
                    //新协议3.0
                    if (item[0] != "string") {
                        args += \'["\' + item[1] + \'","\' + item[0] + \'"]\';
                    } else {
                        args += \'["\' + item[1] + \'"]\';
                    }
                    if (i < obj.list.length - 1) args += ",";
                }
            } else {//enum
                args = \'[\';
                for (var i = 0; i < obj.list.length; i++) {
                    var item = obj.list[i];
                    args += \'"\' + item[0] + \'"\';
                    if (i < obj.list.length - 1) args += ",";
                }
                args += \']\';
            }

            var all = str + (obj.type ? enumfun : fun) + \'("\' + obj.name + \'",\' + args + \'));\';
            //  console.log(all);
            $("#result").append(all + "<br>");
        }
    </script>

然后页面上会得到:

红色部分复制到工程里面就可以用了。当然要带上createProto和createEnum两个方法。proto的格式要规范,毕竟start里面是以空格split的。相对于protobuf.load("xx.proto",callback)的方式要好很多。load对位置要求比较死板,一定要在根目录。而且有类型不存在就会报错,终止程序。add方法不存在找不到类型的错误。另外速度也快了很多。 

 

以上是关于自动生成proto Js语句的主要内容,如果未能解决你的问题,请参考以下文章

20个简洁的 JS 代码片段

js中的Object.assign接受两个函数为参数的时候会发生什么?

[AndroidStudio]_[初级]_[配置自动完成的代码片段]

[AndroidStudio]_[初级]_[配置自动完成的代码片段]

[AndroidStudio]_[初级]_[配置自动完成的代码片段]

vscode代码片段生成vue模板