接上一篇文章:添加链接描述基于Vue3+Ts+Vite项目中grpc-Web的应用以及其中的坑
继续更新一下grpc-web在前端vue项目中的封装思路,仅是个人封装思路,有更好的封装代码可以@我学习一下谢谢。
首先建议先看上一篇文章:添加链接描述基于Vue3+Ts+Vite项目中grpc-Web的应用以及其中的坑
正文开始
在使用 gRPC-Web 时,为了更好地管理和封装 gRPC 客户端调用,可以借助 TypeScript 提供的类型系统和面向对象的封装思想,编写一个通用的 gRPC 客户端管理模块。通过封装,将 gRPC 客户端的创建、请求数据的动态处理、响应的映射等逻辑提取出来,便于多次调用和维护。
代码结构
- GrpcClientManager.ts:gRPC 客户端管理器,负责创建和缓存 gRPC 客户端,避免重复实例化。
- GrpcService.ts:通用的 gRPC 调用服务,封装了对不同 gRPC 方法的动态调用逻辑,简化请求和响应的处理。
- CalculationService.ts:具体业务服务(如计算服务),通过
GrpcService
实现对 gRPC 接口的调用,业务逻辑集中在这里。
GrpcClientManager.ts
// services/GrpcClientManager.tsclassGrpcClientManager{// 静态变量,用于存储 gRPC 客户端,使用 Map 数据结构以支持多个不同服务的客户端privatestatic clients: Map<string,any>=newMap()// 静态方法,用于获取或注册 gRPC 客户端publicstaticgetClient<T>(serviceName:string,clientFactory:()=>T):T{// 如果客户端已存在,直接返回缓存的客户端if(this.clients.has(serviceName)){returnthis.clients.get(serviceName)asT}// 如果客户端不存在,创建新客户端并注册到 clients Map 中const client =clientFactory()this.clients.set(serviceName, client)return client
}}// 导出 GrpcClientManager 供其他模块使用exportdefault GrpcClientManager
作用:
GrpcClientManager
管理所有的 gRPC 客户端实例,确保每个 gRPC 服务只会创建一个客户端,避免重复实例化,提高性能。- 使用
Map
数据结构来存储不同服务对应的客户端,支持按服务名称动态注册和获取客户端。
GrpcService.ts
// services/GrpcService.tsimport GrpcClientManager from'./GrpcClientManager'// 定义通用的 gRPC 响应接口interfaceGrpcResponse<T=any>{
data:T
error?:any}classGrpcService{private client:any// 保存 gRPC 客户端实例constructor(private serviceName:string,clientFactory:()=>any){// 从 GrpcClientManager 获取 gRPC 客户端this.client = GrpcClientManager.getClient<any>(serviceName, clientFactory)}// 通用的 gRPC 调用方法封装publicasyncgrpcCall<TRequest, TResponse>(
methodName:string,// gRPC 方法名称
requestData: Record<string,any>,// 请求数据,键值对形式createRequest:()=> TRequest,// 请求对象的创建函数responseMapper:(response: TResponse)=>any,// 响应映射函数):Promise<GrpcResponse<any>>{returnnewPromise((resolve, reject)=>{const request =createRequest()// 创建请求对象// 动态设置请求数据,调用 gRPC 请求对象的 setter 方法
Object.keys(requestData).forEach((key)=>{const setter =`set${key.charAt(0).toUpperCase()+ key.slice(1)}`// 生成 setter 方法名if(typeof(request asany)[setter]==='function'){(request asany)[setter](requestData[key])// 调用 setter 设置值}else{console.warn(`No setter found for key: ${key}`)}})// 获取 gRPC 客户端方法const clientMethod =this.client[methodName]if(!clientMethod){returnreject(newError(`Method ${methodName} not found on client.`))}// 调用 gRPC 服务方法clientMethod.call(this.client, request,{},(err:any, response: TResponse)=>{if(err){console.error('gRPC Error:', err)returnreject(err)}// 通过映射器处理响应const mappedResponse =responseMapper(response)resolve({ data: mappedResponse })})})}}exportdefault GrpcService
作用:
GrpcService
封装了通用的 gRPC 调用逻辑,支持传入请求数据、动态生成 gRPC 请求对象、调用 gRPC 服务并处理响应。grpcCall
方法通过动态构建 setter 方法来设置请求参数,并灵活处理服务响应。
CalculationService.ts
// services/CalculationService.tsimport*as api from'@generated/api_pb.js'import{ CalculationServiceClient }from'@generated/ApiServiceClientPb'import GrpcService from'../GrpcService'classCalculationService{private grpcService: GrpcService
constructor(){// 初始化 gRPC 服务,创建计算服务的客户端this.grpcService =newGrpcService('CalculationService',()=>newCalculationServiceClient('/api'))}// 处理加法操作publicasyncadd(data:{ num1:number, num2:number}):Promise<number>{returnthis.grpcService.grpcCall<api.AddRequest, api.AddResponse>('add',// gRPC 方法名
data,// 动态输入数据()=>newapi.AddRequest(),// 请求对象生成函数
response => response.getResult(),// 响应映射函数).then(res => res.data).catch((err)=>{console.error('Add error:', err)thrownewError('Failed to perform addition')})}// 处理减法操作publicasyncsubtract(data:{ num1:number, num2:number}):Promise<number>{returnthis.grpcService.grpcCall<api.SubtractRequest, api.SubtractResponse>('subtract',// gRPC 方法名
data,// 请求数据()=>newapi.SubtractRequest(),// 创建请求对象
response => response.getResult(),// 映射响应结果).then(res => res.data).catch((err)=>{console.error('Subtract error:', err)thrownewError('Failed to perform subtraction')})}}exportdefaultnewCalculationService()
作用:
CalculationService
封装了具体的加法和减法操作,通过GrpcService
实现了 gRPC 服务的调用。- 加法和减法操作分别调用
GrpcService
提供的grpcCall
方法,并对请求和响应进行处理。
调用示例
try{const response =await CalculationService.add({ num1: num1.value, num2: num2.value })
result.value = response
console.log('计算结果:', result.value)}catch(err){
error.value ='计算出错!'console.error(err)}
作用:
- 在 Vue 组件中,通过
CalculationService
的add
方法调用 gRPC 服务进行加法计算,捕获异常并处理。
总结
通过这种封装方式,可以在 Vue3 + TypeScript 项目中高效地管理和调用 gRPC 服务:
- 客户端管理:通过
GrpcClientManager
集中管理 gRPC 客户端,避免重复创建。 - 通用服务:
GrpcService
封装了通用的 gRPC 调用逻辑,支持动态处理请求数据和响应映射。 - 具体业务实现:在
CalculationService
中封装业务逻辑,通过GrpcService
实现具体的 gRPC 调用。 - 如果我们有多个proto文件生成的代码,就可以在创建一个CalculationService,新建一个modules包来管理它们 。
- 代码第一版是这样考虑的,还有许多需要优化的地方,交给时间。
番外
其实我是想创建一个流式调用的service,代码写好了之后调用发现没有proto自动生成的代码中无法生成stream的方法。这个还没研究出是咋回事。也没搜到相关解决方案。暂且搁置。
完结
版权归原作者 寒烟说 所有, 如有侵权,请联系我们删除。