vue中使用protobuf踩坑記

因公司需求需使用protobuf,而後就有了這個踩坑記錄javascript

什麼是protobuf?

官方解釋爲:前端

Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the "old" format.vue

翻譯是(機翻---我英語很差)java

協議緩衝區是用於序列化結構化數據的靈活,高效的自動化機制 - 思考XML,但更小,更快,更簡單。您能夠定義一次數據的結構,而後您可使用特殊的源代碼輕鬆地將結構化數據寫入各類數據流並使用各類語言讀取和讀取數據。您甚至能夠更新您的數據結構,而不會中斷根據「舊」格式編譯的已部署程序。webpack

特色:ios

  • 更簡單
  • 是3到10倍小
  • 速度要快20到100倍
  • 不太模糊
  • 生成更易於以編程方式使用的數據訪問類

代碼

在github上寫了個demo demo地址 有須要的能夠下載下來跑一下就理解了。PS:若是以爲有用 請給我個小星星 (筆芯~)git

使用

其實最開始我嘗試使用一個第三方JS protobuf.js protobuf.load的時候瀏覽器報了個錯illegal token '<' (/demo.proto, line 1)查找了下官網issue,大意應該是proto文件多了個字符,可是我查看過proto文件並無發現有多的'<',怎麼辦呢,最後放棄使用第三方。用官方提供的方法。github

下載protobuf編譯器

下載地址 (我下載的是3.40版) github也提供了zip包,可自行下載 (目前最新版本是v3.6.0) 用來編譯proto爲JS文件方便調用web

配置環境變量

因爲公司用的是win10 只須要將下載的文件地址添加到path便可 Mac與window命令惟一的區別就是須要將protoc改爲protoc.exe 前提是須要添加環境變量 配置環境可參考這篇文章vue-cli

編寫proto文件

爲了確保先後一致,下面是後臺寫給個人一個測試proto,我司後臺是java

syntax = "proto2";//protobuf版本
option java_package = "com.test.protobuf";
option java_outer_classname = "PersonMessage";
message Person {
  required int32 id = 1;
  optional string name = 2;
  optional string email = 3;
  repeated string list = 4;
  extensions 100 to 1000;//容許擴展的ID
}

message PersonTree {
  optional string id = 1;
  optional string title = 2;
  repeated PersonTree childs = 3;
}

extend Person {
  optional int32 count = 101;
  optional int32 likes_cnt= 102;
}

message PersonEx {
  optional int32 id = 1;
  extend Person {
    optional int32 px = 103;
    optional int32 py= 104;
  }
  optional Person p = 2;
}
複製代碼

使用vue-cli構建一個工程目錄

npm install -g vue-cli
vue init webpack my-project
cd my-project
npm install
npm run dev
複製代碼

安裝插件:npm install axios element-ui google-protobuf --save

編譯proto爲JS

進入awesome.proto的存放路徑 使用以下命令 protoc.exe --js_out=import_style=commonjs,binary:. awesome.proto

  • 會生成一個awesome_pb.js文件
  • 點擊查看awesome_pb.js其實能夠看到裏面是生成好的方法。只須要在頁面中引入JS調用便可

以後咱們將這個文件引入頁面,固然你也能夠考慮全局引用

測試

本地測試

編寫一個測試頁面,建立一個測試按鈕 我是在測試頁面import messages from './awesome_pb.js' 方法爲:

methods: {
    protobufferTest () {
      var message = new messages.Person() // 調用Person對象 實例化
      // 賦值
      message.setId(23)
      message.setName('asd')
      // 序列化
      var bytes = message.serializeBinary()

      console.log(bytes) // Uint8Array(7) [8, 23, 18, 3, 97, 115, 100]

      // 反序列化
      var message2 = messages.Person.deserializeBinary(bytes)

      console.log(message2) // proto.PersonTree {wrappers_: null, messageId_: undefined, arrayIndexOffset_: -1, array: Array(3), pivot_: 1.7976931348623157e+308, …}

    }
  }
複製代碼

到此,本地測試完成,沒什麼毛病了。

先後端聯調測試

前方有坑

先後傳輸是使用的FormData,而後悲劇的事情來了。後臺解析不了。查看了下數據 [8, 23, 18, 3, 97, 115, 100]確實是傳過去了。 後來排查出緣由是應該是解析成了字符串,而後數值變了。因此解析不出來。 後來使用fromCharCode()方法編輯成字符串形式傳輸給後臺。在使用charCodeAt()取值。

此方法已棄用
protobufferTest () {
      var message = new messages.Person() // 調用Person對象 實例化
      // 賦值
      message.setId(23)
      message.setName('asd')
      // 序列化
      var bytes = message.serializeBinary()

      console.log(bytes) // Uint8Array(7) [8, 23, 18, 3, 97, 115, 100]

      var tests = ''
      for (let index = 0; index < bytes.length; index++) {
        tests += String.fromCharCode(bytes[index])
      }
      console.log(tests) // asd

      // 存入FormData
      let uploadDatas = new FormData()
      uploadDatas.append('protobuf', tests)

      // 使用axios傳輸給後臺
      this.axios.post('/test', uploadDatas)
        .then(function (response) {
          // 將傳回的字符串轉爲數組
          console.log(response.data.split('')) // ["↵", "", "3", "2", "", "", "a", "s", "d", "f"]
          let str = response.data.split('')
          let toChar = []
          for (let index = 0; index < str.length; index++) {
            toChar.push(str[index].charCodeAt())
          }
          console.log(toChar) // [10, 2, 51, 50, 18, 4, 97, 115, 100, 102]

          // 後臺傳回來的是PersonTree裏面的值因此調用PersonTree來反序列化
          var message2 = messages.PersonTree.deserializeBinary(toChar)

          console.log(message2) // proto.PersonTree {wrappers_: null, messageId_: undefined, arrayIndexOffset_: -1, array: Array(3), pivot_: 1.7976931348623157e+308, …}

          // 獲取PersonTree的id值
          console.log(message2.getId()) // 32
        })
        .catch(function (error) {
          console.log(error)
        })

    }
複製代碼

以上方法可能存在安全隱患。 向後端傳值 由於FormData支持兩種方式傳輸string和blob因此將bytes存入blob中 前端獲取數據 對axios的默認傳輸方式作個更改 axios.defaults.responseType = 'arraybuffer' 將以上的JS代碼更改成如下內容

protobufferTest () {
      var message = new messages.Person()
      message.setId(23)
      message.setName('asd')
      var bytes = message.serializeBinary()
      
      console.log(bytes)
      let uploadDatas = new FormData()
      var blob = new Blob([bytes], {type: 'application/octet-stream'})

      uploadDatas.append('protobuf', blob)
      
      this.axios.post('/test', uploadDatas)
        .then(function (response) {
          console.log(response)

          var message2 = messages.PersonTree.deserializeBinary(response.data)
          console.log(message2.getId())
        })
        .catch(function (error) {
          console.log(error)
        })
      // console.log(bytes)
    }

複製代碼

至此先後聯調完成

參考文檔

前端後臺以及遊戲中使用google-protobuf詳解

javascript前端如何使用google-protobuf

相關文章
相關標籤/搜索