最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue中使用protobuf的過程記錄

 更新時(shí)間:2018年10月26日 14:22:42   作者:fengxianqi  
由于目前公司采用了ProtoBuf做前后端數(shù)據(jù)交互,進(jìn)公司以來一直用的是公司大神寫好的基礎(chǔ)庫,完全不了解底層是如何解析的。下面小編給大家分享vue中使用protobuf的過程記錄,需要的朋友參考下吧

由于目前公司采用了ProtoBuf做前后端數(shù)據(jù)交互,進(jìn)公司以來一直用的是公司大神寫好的基礎(chǔ)庫,完全不了解底層是如何解析的,一旦報(bào)錯(cuò)只能求人,作為一只還算有鉆研精神的猿,應(yīng)該去了解一下底層的實(shí)現(xiàn),在這里記錄一下學(xué)習(xí)過程。

Protobuf簡(jiǎn)單介紹

Google Protocol Buffer(簡(jiǎn)稱 Protobuf)是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,平臺(tái)無關(guān)、語言無關(guān)、可擴(kuò)展,可用于通訊協(xié)議和數(shù)據(jù)存儲(chǔ)等領(lǐng)域。

有幾個(gè)優(yōu)點(diǎn):

1.平臺(tái)無關(guān),語言無關(guān),可擴(kuò)展;
2.提供了友好的動(dòng)態(tài)庫,使用簡(jiǎn)單;
3.解析速度快,比對(duì)應(yīng)的XML快約20-100倍;
4.序列化數(shù)據(jù)非常簡(jiǎn)潔、緊湊,與XML相比,其序列化之后的數(shù)據(jù)量約為1/3到1/10。

個(gè)人感受: 前后端數(shù)據(jù)傳輸用json還是protobuf其實(shí)對(duì)開發(fā)來說沒啥區(qū)別,protobuf最后還是要解析成json才能用。個(gè)人覺得比較好的幾點(diǎn)是:

1.前后端都可以直接在項(xiàng)目中使用protobuf,不用再額外去定義model;
2.protobuf可以直接作為前后端數(shù)據(jù)和接口的文檔,大大減少了溝通成本;

沒有使用protobuf之前,后端語言定義的接口和字段,前端是不能直接使用的,前后端溝通往往需要維護(hù)一份接口文檔,如果后端字段有改動(dòng),需要去修改文檔并通知前端,有時(shí)候文檔更新不及時(shí)或容易遺漏,溝通成本比較大。 使用protobuf后,protobuf文件由后端統(tǒng)一定義, protobuf直接可以作為文檔 ,前端只需將protobuf文件拷貝進(jìn)前端項(xiàng)目即可。如果后端字段有改動(dòng),只需通知前端更新protobuf文件即可,因?yàn)楹蠖耸侵苯邮褂昧藀rotobuf文件,因此protobuf文件一般是不會(huì)出現(xiàn)遺漏或錯(cuò)誤的。長(zhǎng)此以往,團(tuán)隊(duì)合作效率提升是明顯的。

廢話了一大堆,下面進(jìn)入正題。 我這里講的主要是在vue中的使用,是目前本人所在的公司項(xiàng)目實(shí)踐,大家可以當(dāng)做參考。

思路

前端中需要使用 protobuf.js 這個(gè)庫來處理proto文件。

protobuf.js 提供了幾種方式來處理proto。

protobuf.load("awesome.proto", function(err, root) {...})
protobuf.load("awesome.json", function(err, root) {...})

眾所周知,vue項(xiàng)目build后生成的dist目錄中只有html,css,js,images等資源,并不會(huì)有 .proto 文件的存在,因此需要用 protobuf.js 這個(gè)庫將 *.proto 處理成 *.js 或 *.json ,然后再利用庫提供的方法來解析數(shù)據(jù),最后得到數(shù)據(jù)對(duì)象。

PS: 實(shí)踐發(fā)現(xiàn),轉(zhuǎn)化為js文件會(huì)更好用一些,轉(zhuǎn)化后的js文件直接在原型鏈上定義了一些方法,非常方便。因此后面將會(huì)是使用這種方法來解析proto。

預(yù)期目標(biāo)

在項(xiàng)目中封裝一個(gè) request.js 模塊,希望能像下面這樣使用,調(diào)用api時(shí)只需指定請(qǐng)求和響應(yīng)的model,然后傳遞請(qǐng)求參數(shù),不需關(guān)心底層是如何解析proto的,api返回一個(gè)Promise對(duì)象:

// /api/student.js 定義接口的文件
import request from '@/lib/request'

// params是object類型的請(qǐng)求參數(shù)
// school.PBStudentListReq 是定義好的請(qǐng)求體model
// school.PBStudentListRsp 是定義好的響應(yīng)model
// getStudentList 是接口名稱
export function getStudentList (params) {
 const req = request.create('school.PBStudentListReq', params)
 return request('getStudentList', req, 'school.PBStudentListRsp')
}

// 在HelloWorld.vue中使用
import { getStudentList } from '@/api/student'
export default {
 name: 'HelloWorld',
 created () {

 },
 methods: {
 _getStudentList () {
  const req = {
  limit = 20,
  offset = 0
  }
  getStudentList(req).then((res) => {
  console.log(res)
  }).catch((res) => {
  console.error(res)
  })
 }
 }
}

準(zhǔn)備工作

1.拿到一份定義好的proto文件。

雖然語法簡(jiǎn)單,但其實(shí)前端不用怎么關(guān)心如何寫proto文件,一般都是由后端來定義和維護(hù)。在這里大家可以直接用一下我定義好的一份 demo 。

// User.proto
package framework;
syntax = "proto3";

message PBUser {
 uint64 user_id = 0;
 string name = 1;
 string mobile = 2;
}

// Class.proto
package school;
syntax = "proto3";

message PBClass {
 uint64 classId = 0;
 string name = 1;
}

// Student.proto
package school;
syntax = "proto3";

import "User.proto";
import "Class.proto";

message PBStudent {
 uint64 studentId = 0;
 PBUser user = 1;
 PBClass class = 2;
 PBStudentDegree degree = 3;
}

enum PBStudentDegree {
 PRIMARY = 0; // 小學(xué)生
 MIDDLE = 1; // 中學(xué)生
 SENIOR = 2; // 高中生
 COLLEGE = 3; // 大學(xué)生
}

message PBStudentListReq {
 uint32 offset = 1;
 uint32 limit = 2;
}

message PBStudentListRsp {
 repeated PBStudent list = 1;
}


// MessageType.proto
package framework;
syntax = "proto3";
// 公共請(qǐng)求體
message PBMessageRequest {
 uint32 type = 1;       // 消息類型
 bytes messageData = 2;      // 請(qǐng)求數(shù)據(jù)
 uint64 timestamp = 3;      // 客戶端時(shí)間戳
 string version = 4;       // api版本號(hào)

 string token = 14;       // 用戶登錄后服務(wù)器返回的 token,用于登錄校驗(yàn)
}

// 消息響應(yīng)包
message PBMessageResponse {
 uint32 type = 3;       // 消息類型
 bytes messageData = 4;      // 返回?cái)?shù)據(jù)

 uint32 resultCode = 6;      // 返回的結(jié)果碼
 string resultInfo = 7;      // 返回的結(jié)果消息提示文本(用于錯(cuò)誤提示)
}
// 所有的接口
enum PBMessageType {
 // 學(xué)生相關(guān)
 getStudentList = 0;       // 獲取所有學(xué)生的列表, PBStudentListReq => PBStudentListRsp
}

其實(shí)不用去學(xué)習(xí)proto的語法都能一目了然。這里有兩種命名空間 framework 和 school , PBStudent 引用了 PBUser ,可以認(rèn)為 PBStudent 繼承了 PBUser 。

一般來說,前后端需要統(tǒng)一約束一個(gè)請(qǐng)求model和響應(yīng)model,比如請(qǐng)求中哪些字段是必須的,返回體中又有哪些字段,這里用 MessageType.proto 的 PBMessageRequest 來定義請(qǐng)求體所需字段, PBMessageResponse 定義為返回體的字段。

PBMessageType 是接口的枚舉,后端所有的接口都寫在這里,用注釋表示具體請(qǐng)求參數(shù)和返回參數(shù)類型。比如這里只定義了一個(gè)接口 getStudentList 。

拿到后端提供的這份 *.proto 文件后,是不是已經(jīng)可以基本了解到:有一個(gè) getStudentList 的接口,請(qǐng)求參數(shù)是 PBStudentListReq ,返回的參數(shù)是 PBStudentListRsp 。

所以說proto文件可以直接作為前后端溝通的文檔。

步驟

1.新建一個(gè)vue項(xiàng)目

同時(shí)添加安裝 axios 和 protobufjs 。

# vue create vue-protobuf
# npm install axios protobufjs --save-dev

2.在 src 目錄下新建一個(gè) proto 目錄,用來存放 *.proto 文件,并將寫好的proto文件拷貝進(jìn)去。

此時(shí)的項(xiàng)目目錄和 package.json :

3.將 *.proto 文件生成 src/proto/proto.js (重點(diǎn))

protobufjs 提供了一個(gè)叫 pbjs 的工具,這是一個(gè)神器,根據(jù)參數(shù)不同可以打包成xx.json或xx.js文件。比如我們想打包成json文件,在根目錄運(yùn)行:

npx pbjs -t json src/proto/*.proto > src/proto/proto.json

可以在 src/proto 目錄下生成一個(gè)proto.json文件,查看請(qǐng)點(diǎn)擊這里。 之前說了:實(shí)踐證明打包成js模塊才是最好用的。我這里直接給出最終的命令

npx pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto

-w 參數(shù)可以指定打包js的包裝器,這里用的是commonjs,詳情請(qǐng)各位自己去看文檔。運(yùn)行命令后在src/proto目錄下生成的 proto.js 。在chrome中 console.log(proto.js) 一下:

可以發(fā)現(xiàn),這個(gè)模塊在原型鏈上定義了 load , lookup
等非常有用的api,這正是后面我們將會(huì)用到的。 為以后方便使用,我們將命令添加到package.json的script中:

"scripts": {
 "serve": "vue-cli-service serve",
 "build": "vue-cli-service build",
 "lint": "vue-cli-service lint",
 "proto": "pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto"
 },

以后更新proto文件后,只需要 npm run proto 即可重新生成最新的proto.js。

4. 封裝request.js

在前面生成了proto.js文件后,就可以開始封裝與后端交互的基礎(chǔ)模塊了。首先要知道,我們這里是用axios來發(fā)起http請(qǐng)求的。

整個(gè)流程:開始調(diào)用接口 -> request.js將數(shù)據(jù)變成二進(jìn)制 -> 前端真正發(fā)起請(qǐng)求 -> 后端返回二進(jìn)制的數(shù)據(jù) -> request.js處理二進(jìn)制數(shù)據(jù) -> 獲得數(shù)據(jù)對(duì)象。

可以說request.js相當(dāng)于一個(gè)加密解密的中轉(zhuǎn)站。在 src/lib 目錄下添加一個(gè) request.js 文件,開始開發(fā):

既然我們的接口都是二進(jìn)制的數(shù)據(jù),所以需要設(shè)置axios的請(qǐng)求頭,使用arraybuffer,如下:

import axios from 'axios'
const httpService = axios.create({
 timeout: 45000,
 method: 'post',
 headers: {
 'X-Requested-With': 'XMLHttpRequest',
 'Content-Type': 'application/octet-stream'
 },
 responseType: 'arraybuffer'
})

MessageType.proto 里面定義了與后端約定的接口枚舉、請(qǐng)求體、響應(yīng)體。發(fā)起請(qǐng)求前需要將所有的請(qǐng)求轉(zhuǎn)換為二進(jìn)制,下面是request.js的主函數(shù)

import protoRoot from '@/proto/proto'
import protobuf from 'protobufjs'
// 請(qǐng)求體message
const PBMessageRequest = protoRoot.lookup('framework.PBMessageRequest')
// 響應(yīng)體的message
const PBMessageResponse = protoRoot.lookup('framework.PBMessageResponse')
const apiVersion = '1.0.0'
const token = 'my_token'
function getMessageTypeValue(msgType) {
 const PBMessageType = protoRoot.lookup('framework.PBMessageType')
 const ret = PBMessageType.values[msgType]
 return ret
}
/**
 * 
 * @param {*} msgType 接口名稱
 * @param {*} requestBody 請(qǐng)求體參數(shù)
 * @param {*} responseType 返回值
 */
function request(msgType, requestBody, responseType) { 
 // 得到api的枚舉值
 const _msgType = getMessageTypeValue(msgType)
 // 請(qǐng)求需要的數(shù)據(jù)
 const reqData = {
 timeStamp: new Date().getTime(),
 type: _msgType,
 version: apiVersion,
 messageData: requestBody,
 token: token
 }
}
 // 將對(duì)象序列化成請(qǐng)求體實(shí)例
 const req = PBMessageRequest.create(reqData)
 // 調(diào)用axios發(fā)起請(qǐng)求
 // 這里用到axios的配置項(xiàng):transformRequest和transformResponse
 // transformRequest 發(fā)起請(qǐng)求時(shí),調(diào)用transformRequest方法,目的是將req轉(zhuǎn)換成二進(jìn)制
 // transformResponse 對(duì)返回的數(shù)據(jù)進(jìn)行處理,目的是將二進(jìn)制轉(zhuǎn)換成真正的json數(shù)據(jù)
 return httpService.post('/api', req, {
 transformRequest,
 transformResponse: transformResponseFactory(responseType)
 }).then(({data, status}) => {
 // 對(duì)請(qǐng)求做處理
 if (status !== 200) {
  const err = new Error('服務(wù)器異常')
  throw err
 }
 console.log(data)
 },(err) => {
 throw err
 })
}
// 將請(qǐng)求數(shù)據(jù)encode成二進(jìn)制,encode是proto.js提供的方法
function transformRequest(data) {
 return PBMessageRequest.encode(data).finish()
}
function isArrayBuffer (obj) {
 return Object.prototype.toString.call(obj) === '[object ArrayBuffer]'
}
function transformResponseFactory(responseType) {
 return function transformResponse(rawResponse) {
 // 判斷response是否是arrayBuffer
 if (rawResponse == null || !isArrayBuffer(rawResponse)) {
  return rawResponse
 }
 try {
  const buf = protobuf.util.newBuffer(rawResponse)
  // decode響應(yīng)體
  const decodedResponse = PBMessageResponse.decode(buf)
  if (decodedResponse.messageData && responseType) {
  const model = protoRoot.lookup(responseType)
  decodedResponse.messageData = model.decode(decodedResponse.messageData)
  }
  return decodedResponse
 } catch (err) {
  return err
 }
 }
}
// 在request下添加一個(gè)方法,方便用于處理請(qǐng)求參數(shù)
request.create = function (protoName, obj) {
 const pbConstruct = protoRoot.lookup(protoName)
 return pbConstruct.encode(obj).finish()
}
// 將模塊暴露出去
export default request

最后寫好的具體代碼請(qǐng)看: request.js 。 其中用到了 lookup() , encode() , finish() , decode() 等幾個(gè)proto.js提供的方法。

5. 調(diào)用request.js

在.vue文件直接調(diào)用api前,我們一般不直接使用request.js來直接發(fā)起請(qǐng)求,而是將所有的接口再封裝一層,因?yàn)橹苯邮褂胷equest.js時(shí)要指定請(qǐng)求體,響應(yīng)體等固定的值,多次使用會(huì)造成代碼冗余。

我們習(xí)慣上在項(xiàng)目中將所有后端的接口放在 src/api 的目錄下,如針對(duì)student的接口就放在 src/api/student.js 文件中,方便管理。 將 getStudentList 的接口寫在 src/api/student.js 中

import request from '@/lib/request'
// params是object類型的請(qǐng)求參數(shù)
// school.PBStudentListReq 是定義好的請(qǐng)求體model
// school.PBStudentListRsp 是定義好的響應(yīng)model
// getStudentList 是接口名稱
export function getStudentList (params) {
 const req = request.create('PBStudentListReq', params)
 return request('getStudentList', req, 'school.PBStudentListRsp')
}
// 后面如果再添加接口直接以此類推
export function getStudentById (id) {
 // const req = ...
 // return request(...)
}

6. 在.vue中使用接口

需要哪個(gè)接口,就import哪個(gè)接口,返回的是Promise對(duì)象,非常方便。

<template>
 <div class="hello">
 <button @click="_getStudentList">獲取學(xué)生列表</button>
 </div>
</template>
<script>
import { getStudentList } from '@/api/student'
export default {
 name: 'HelloWorld',
 methods: {
 _getStudentList () {
  const req = {
  limit: 20,
  offset: 0
  }
  getStudentList(req).then((res) => {
  console.log(res)
  }).catch((res) => {
  console.error(res)
  })
 }
 },
 created () {
 }
}
</script>
<style lang="scss">
</style>

總結(jié)

整個(gè)demo的代碼:demo

前端使用的整個(gè)流程:

1. 將后端提供的所有的proto文件拷進(jìn) src/proto 文件夾
2. 運(yùn)行 npm run proto 生成proto.js
3. 根據(jù)接口枚舉在 src/api 下寫接口
4. .vue 文件中使用接口。
(其中1和2可以合并在一起寫一個(gè)自動(dòng)化的腳本,每次更新只需運(yùn)行一下這個(gè)腳本即可)。

寫的比較啰嗦,文筆也不好,大家見諒。

這個(gè)流程就是我感覺比較好的一個(gè)proto在前端的實(shí)踐,可能并不是最好,如果在你們公司有其他更好的實(shí)踐,歡迎大家一起交流分享。

后續(xù)

在vue中使用是需要打包成一個(gè)js模塊來使用比較好(這是因?yàn)関ue在生產(chǎn)環(huán)境中打包成只有html,css,js等文件)。但在某些場(chǎng)景,比如在Node環(huán)境中,一個(gè)Express的項(xiàng)目,生產(chǎn)環(huán)境中是允許出現(xiàn) .proto 文件的,這時(shí)候可以采取 protobuf.js 提供的其他方法來動(dòng)態(tài)解析proto,不再需要npm run proto這種操作了。

以上所述是小編給大家介紹的vue中使用protobuf的過程記錄,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Vue路由組件通過props配置傳參的實(shí)現(xiàn)

    Vue路由組件通過props配置傳參的實(shí)現(xiàn)

    本文主要介紹了Vue路由組件通過props配置傳參的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • vue以組件或者插件的形式實(shí)現(xiàn)throttle或者debounce

    vue以組件或者插件的形式實(shí)現(xiàn)throttle或者debounce

    這篇文章主要介紹了vue以組件或者插件的形式實(shí)現(xiàn)throttle或者debounce,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-05-05
  • Vue3中watch的用法與最佳實(shí)踐指南

    Vue3中watch的用法與最佳實(shí)踐指南

    這篇文章主要給大家介紹了關(guān)于Vue3中watch用法與最佳實(shí)踐的相關(guān)資料,watch的作用可以監(jiān)控一個(gè)值的變換,并調(diào)用因?yàn)樽兓枰獔?zhí)行的方法,可以通過watch動(dòng)態(tài)改變關(guān)聯(lián)的狀態(tài),需要的朋友可以參考下
    2021-07-07
  • Vue3新狀態(tài)管理工具實(shí)例詳解

    Vue3新狀態(tài)管理工具實(shí)例詳解

    Vue3公布曾經(jīng)有一段時(shí)間了,它采納了新的響應(yīng)式零碎,而且構(gòu)建了一套全新的 Composition API,下面這篇文章主要給大家介紹了關(guān)于Vue3新狀態(tài)管理工具的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • Vue登錄功能的實(shí)現(xiàn)流程詳解

    Vue登錄功能的實(shí)現(xiàn)流程詳解

    本文主要介紹了Vue實(shí)現(xiàn)登錄功能全套詳解(含封裝axios),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • element-plus的el-table自定義表頭篩選查詢功能實(shí)現(xiàn)

    element-plus的el-table自定義表頭篩選查詢功能實(shí)現(xiàn)

    這篇文章主要介紹了element-plus的el-table自定義表頭篩選查詢功能實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-07-07
  • vue項(xiàng)目基于WebRTC實(shí)現(xiàn)一對(duì)一音視頻通話

    vue項(xiàng)目基于WebRTC實(shí)現(xiàn)一對(duì)一音視頻通話

    這篇文章主要介紹了vue項(xiàng)目基于WebRTC實(shí)現(xiàn)一對(duì)一音視頻通話效果,實(shí)現(xiàn)代碼分為前端和后端兩部分代碼,需要的朋友可以參考下
    2024-05-05
  • vue微信分享出來的鏈接點(diǎn)開是首頁問題的解決方法

    vue微信分享出來的鏈接點(diǎn)開是首頁問題的解決方法

    這篇文章主要為大家詳細(xì)介紹了vue微信分享出來的鏈接點(diǎn)開是首頁問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Vue項(xiàng)目從webpack3.x升級(jí)webpack4不完全指南

    Vue項(xiàng)目從webpack3.x升級(jí)webpack4不完全指南

    前段時(shí)間,泡面將自己的一個(gè)Vue-cli構(gòu)建的前端框架從webpack3.x升級(jí)到了4.x版本,現(xiàn)在才拉出來記錄一下,已備忘之用,也和大家分享一下,需要的朋友可以參考下
    2019-04-04
  • vue-router跳轉(zhuǎn)頁面的方法

    vue-router跳轉(zhuǎn)頁面的方法

    本篇文章主要介紹了vue-router跳轉(zhuǎn)頁面的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02

最新評(píng)論

民勤县| 昆山市| 班玛县| 阿拉善盟| 京山县| 炉霍县| 洛川县| 玉林市| 马尔康县| 延庆县| 伊春市| 平邑县| 顺义区| 武安市| 叶城县| 阜城县| 桓台县| 柘城县| 兴和县| 陕西省| 青岛市| 河间市| 荔波县| 汶上县| 成安县| 镇平县| 丰镇市| 靖西县| 桦甸市| 扶绥县| 瑞昌市| 伊金霍洛旗| 安陆市| 定兴县| 驻马店市| 呈贡县| 方正县| 孟连| 淄博市| 道孚县| 永年县|