1. 程式人生 > >自動生成proto Js語句

自動生成proto Js語句

地址 tco == 字段 文件 var tar spl 規範

在與後端的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語句