核心思想:
electron通过主进程node模块调用grpc- 借助
preload预加载使用ipc通信主进程调用的grpc- 渲染进程调用
preload预加载脚本暴露的函数实现通信
实现grpc引入:
-
引入生成的
grpc客户端代码,并安装指定依赖:npm i grpc google-protobuf @grpc/proto-loader -
构造
grpc代码调用/** * [ grpcClient.js ] * GRPC接口定义,待主进程调用之后与preload预加载脚本通信 */ const grpc = require('grpc'); const { FileUploadServiceClient } = require('../grpc/Upload_grpc_pb'); //gRPC 客户端文件路径 const ip = '192.168.100.189:50051' //创建一个连接 const createInsecure = grpc.credentials.createInsecure() /** * 创建 gRPC 客户端实例并指定 gRPC 服务端点和凭据 */ function createGRPCClientSet() { const grpcClient = new FileUploadServiceClient(ip, createInsecure) return grpcClient } // 调用grpc接口getUploadList function getUploadList(request) { return new Promise((resolve, reject) => { const grpcClient = createGRPCClientGet(); grpcClient.getUploadList(request, (error, response) => { if (error) { reject(error); // 如果发生错误,拒绝 Promise } else { resolve(response); // 如果成功,解决 Promise } }); }); } module.exports = { getUploadList } -
主进程调用
grpcClient.js文件定义的grpc调用函数待后续与preload通信async function createWindow() { // 在初始化窗口的时候,通过 IPC 通信触发 Node.js 服务的操作 require('./isMain/GrpcToBackground') const win = new BrowserWindow({ width: 1120, height: 680, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, }, autoHideMenuBar: false, // 隐藏顶部工具栏,生产环境时设置为true // frame: false // 无边框 }) if (process.env.WEBPACK_DEV_SERVER_URL) { await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) if (!process.env.IS_TEST) win.webContents.openDevTools() } else { createProtocol('app') win.loadURL('app://./index.html') } }在此将主进程
node调用grpc这一步封装来优化代码,require('./isMain/GrpcToBackground')文件中实现 -
GrpcToBackground.js/** * [ GrpcToBackground.js ] * 在background主进程中调用grpc代码, 待preload预加载脚本通信 */ import { ipcMain } from 'electron'; var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js') var google_protobuf_wrappers_pb = require('google-protobuf/google/protobuf/wrappers_pb.js'); const { NewUploadItem, UploadItemTransInfo, PauseUploadInfo, PauseMethodMsg, TransSetting } = require('../grpc/Upload_pb') // 导入 Node.js 服务代码 const grpcService = require('../api/grpcClient') // 获取上传列表(grpc) ipcMain.handle('getUploadList', async () => { try { const request = new google_protobuf_empty_pb.Empty() const response = await grpcService.getUploadList(request) return ListArray(response) } catch (error) { console.log('error', error) } }) -
此时在主进程中调用了
grpc可能会出现问题,- 找不到
grpc依赖下的electron-v93-win32-x64-unkonwn/grpc_node.node文件 - 这里我将同级的
node-v93-win32-x64-unknown/grpc_node.node拷贝到electron-v93-win32-x64-unkonwn下出现的问题
- 找不到
-
解决: 解决
electron与node兼容性问题-
尝试找到
electron和node相同版本的node_module_version,发现只有10.xxx比较老版本,所以无法使用对应更老版本的node与electron -
所以根据
electron官网提供的编译工具(@electron/rebuild)将重建electron与node适配版本,解决问题 -
具体操作:
-
安装:
npm install --save-dev @electron/rebuild -
看见
package.json中多了rebuild -
使用
npm run rebuild编译 -
可能遇到的问题:包含
gyp或者node-gyp的报错信息解决: 需要安装
pythonv3.11.5下载需要安装 Python 的原因是因为 Electron 使用了 Node.js 的 C++ 模块,而这些模块需要编译成二进制代码以在不同的操作系统上运行。Python 在这里的作用是用于构建这些 C++ 模块,因为在构建过程中可能会涉及到一些编译任务 -
重新运行
npm run rebuild编译
-
-
preload通信grpc:
-
定义
preload文件并使用绝对路径引入到主进程中async function createWindow() { // 在初始化窗口的时候,通过 IPC 通信触发 Node.js 服务的操作 require('./isMain/GrpcToBackground') const win = new BrowserWindow({ width: 1120, height: 680, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, }, autoHideMenuBar: false, // 隐藏顶部工具栏,生产环境时设置为true // frame: false // 无边框 }) }可能会出现的问题: 在打包过后,找不到
preload文件,所以需要额外打包配置(vue.config.js)module.exports = defineConfig({ pluginOptions: { electronBuilder: { preload: 'src/preload/index.js' //指定preload路径 } } }) -
在
preload中,实现通信grpcconst { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('versions', { node: 'electron', /** * GRPC */ // 获取上传文件列表 getUploadListGRPC: () => { return ipcRenderer.invoke('getUploadList') } }) -
此时完成:
- 完成了
grpc的引入 - 完成定义的
grpc在主进程node中调用 - 使用
preload通信主进程中调用的grpc
- 完成了
渲染进程调用:
在vue页面中调用preload暴露的函数,使用window.versions.getUploadListGRPC()
/**
* [ uploading.vue ]
*/
<!-- 正在上传页面 -->
<template>
<div></div>
</template>
<script>
export default {
name: 'downLoading',
data() {
return {
InterTimer: null
}
},
methods: {
getList() {
clearInterval(this.InterTimer)
const updateList = async () => {
// 通过调用preload来实际通信grpc
const res = await window.versions.getUploadListGRPC();
this.fileList = [];
res && res.forEach(item => {
if (item.parentID === "") {
this.fileList.push(item);
} else {
const parent = res.find(parentItem => parentItem.itemID === item.parentID);
if (parent) {
if (!parent.children) parent.children = [];
parent.children.push(item);
}
}
});
if (this.fileList.length === 0) return clearInterval(this.InterTimer)
};
// 立即执行一次
updateList();
// 然后每隔1秒执行一次
this.InterTimer = setInterval(updateList, 1000);
},
},
created() {
this.getList()
},
}
</script>
此时实现了在渲染进程页面中去调用grpc实现通信
grpc类型转换
当调用grpc传参,获取参数的时候都需要转换参数类型:
-
根据
.proto文件定义的消息类型 -
在生成的消息类型
js文件中可以查看具体如何转换参数类型 -
例如:
// grpc getUploadList: { path: '/StateService/GetUploadList', requestStream: false, responseStream: false, requestType: google_protobuf_empty_pb.Empty, // 传参类型 responseType: Upload_pb.UploadList, requestSerialize: serialize_google_protobuf_Empty, requestDeserialize: deserialize_google_protobuf_Empty, responseSerialize: serialize_UploadList, responseDeserialize: deserialize_UploadList, }// 类型转换 var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js') ipcMain.handle('getUploadList', async () => { try { const request = new google_protobuf_empty_pb.Empty() //类型转换为grpc定义入参类型 const response = await grpcService.getUploadList(request) return ListArray(response) } catch (error) { console.log('error', error) } })
至于为什么要response.toObject(),
toObject():是从grpc的消息类型的文件中得知,根据定义的response的类型为Upload_pb.UploadList,因此可以在类型文件中的UploadList原型上去查看他的方法,转为我们客户端可以展示的数据格式。