electron中定义ipc的完美方案
前语
发现在主进程和渲染进程通信的设计中,很多代码都是重复的,导致最后非常臃肿,且不利于后期扩展
electron项目中 核心文件结构如下
| -- index.js
| -- index.html
| -- ipc
| -- handlers
| -- other.js
| -- xxx.js
| -- index.js
| -- preload.js
方案一
ipc/handlers 里放你需要曝漏给渲染进程和主进程的方法,比如other.js
import processa from "node:process"
export const getEnv = ()=>{
return JSON.stringify(processa.env)
}
// export const setXXX = ()=>{
// xxxx
// return xxxx
// }
ipc/index.js里做将handler 内容分别注册到渲染进程和主进程的逻辑
// 此文件不需要动
import path from "node:path";
import { readdirSync } from "node:fs";
import { ipcMain } from "electron";
export const getIcpMainHandler = async () => {
let allHandler = {};
const dirs = readdirSync(path.join(__dirname, "handlers"), "utf8");
for (const file of dirs) {
const filePath = path.join(__dirname, "handlers", file);
const handlers = await import(filePath);
allHandler = {
...allHandler,
...handlers,
};
}
return allHandler;
};
// 主进程中定义(注册) ipcMain.handle方法
export const registerHandlerForIcpMain = async () => {
const ipcMainHandlers = await getIcpMainHandler();
for (const key in ipcMainHandlers) {
const handler = ipcMainHandlers[key];
ipcMain.handle(key, (event, ...params) => handler(...params));
}
};
// 渲染进程定义(注册) ipcMain.handle方法
export const registerHandlerForPreload = async () => {
const ipcMainHandlers = await getIcpMainHandler();
return Object.keys(ipcMainHandlers);
};
// 以上两个方法的汇总
export const registerHandlerForMainAndPreload = async () => {
registerHandlerForIcpMain();
ipcMain.handle("getIcpHandler", registerHandlerForPreload);
};
在主进程的入口处 index.js,调用此方法
...
import {registerHandlerForMainAndPreload } from "./ipc/index.js";
...
app.whenReady().then(() => {
registerHandlerForMainAndPreload();
...
});
在 preload.js , 也需要做一些处理,用来配合将handler注册渲染进程相关逻辑
const { contextBridge, ipcRenderer } = require("electron");
const main = async () => {
if (!window.electronApi) {
// 获取主进程里的ipcMainHandlers
const ipcMainHandlers = await ipcRenderer.invoke("getIcpHandler");
// 注入到渲染进程的electronApi对象中
const electronAPIContent = {};
for (const handlerName of ipcMainHandlers) {
electronAPIContent[handlerName] = function(){return ipcRenderer.invoke(handlerName, ...arguments)}
}
contextBridge.exposeInMainWorld("electronApi", electronAPIContent);
}
};
main();
最后,在渲染进程的页面中,我们就可以用此handler中的api了
window.electronApi.getEnv()
方案二(推荐,这是方案一的优化)
在方案一种我们可以看到 preload.js 顶层是异步,需要包裹一个main方法。
如果不想这么做,可以利用三方包和sendSync结合来实现
ipc>index.js
// 此文件不需要动
import path from "node:path";
import { readdirSync } from "node:fs";
import { ipcMain } from "electron";
import importSync from "import-sync";
export const getIcpMainHandler = () => {
let allHandler = {};
const dirs = readdirSync(path.join(__dirname, "handlers"), "utf8");
for (const file of dirs) {
const filePath = path.join(__dirname, "handlers", file);
const handlers = importSync(filePath);
allHandler = {
...allHandler,
...handlers,
};
}
return allHandler;
};
// 主进程中定义(注册) ipcMain.handle方法
export const registerHandlerForIcpMain = () => {
const ipcMainHandlers = getIcpMainHandler();
for (const key in ipcMainHandlers) {
const handler = ipcMainHandlers[key];
ipcMain.handle(key, (event, ...params) => handler(...params));
}
};
// 渲染进程定义(注册) ipcMain.handle方法
export const registerHandlerForPreload = () => {
const ipcMainHandlers = getIcpMainHandler();
return Object.keys(ipcMainHandlers);
};
// 以上两个方法的汇总
export const registerHandlerForMainAndPreload = () => {
registerHandlerForIcpMain();
ipcMain.on('getIcpHandler', (event, data) => {
event.returnValue = registerHandlerForPreload();
})
};
preload.js
const { contextBridge, ipcRenderer } = require("electron");
if (!window.electronApi) {
// 获取主进程里的ipcMainHandlers
const ipcMainHandlers = ipcRenderer.sendSync("getIcpHandler");
// 注入到渲染进程的electronApi对象中
const electronAPIContent = {
onUsbChange: (callback) =>
ipcRenderer.on("usbChange", (_event, value) => callback(value)),
};
for (const handlerName of ipcMainHandlers) {
electronAPIContent[handlerName] = function () {
return ipcRenderer.invoke(handlerName, ...arguments);
};
}
contextBridge.exposeInMainWorld("electronApi", electronAPIContent);
}
方案二优化
使用了上边一段时间发现 渲染进程中的electronApi拿到的全是函数,导致无无法在 handlers 中定义静态属性,于是做了如下修改
ipc>index.js
// 此文件不需要动
import path from "node:path";
import { readdirSync } from "node:fs";
import { ipcMain } from "electron";
import importSync from "import-sync";
export const getIcpMainHandler = () => {
let allHandler = {};
const dirs = readdirSync(path.join(__dirname, "handlers"), "utf8");
for (const file of dirs) {
const filePath = path.join(__dirname, "handlers", file);
const handlersTemp = importSync(filePath);
let handlers = {};
for (const key in handlersTemp) {
const handler = handlersTemp[key];
let handlerType = Object.prototype.toString.call(handler);
const match = handlerType.match(/^\[object (\w+)\]$/);
handlerType = match[1];
handlers[key] = {
key,
type: handlerType,
val: handler,
};
}
allHandler = {
...allHandler,
...handlers,
};
}
return allHandler;
};
// 主进程中定义(注册) ipcMain.handle方法
export const registerHandlerForIcpMain = () => {
const ipcMainHandlers = getIcpMainHandler();
for (const key in ipcMainHandlers) {
const handler = ipcMainHandlers[key];
if (handler.type.indexOf("Function")>-1) { // Function or AsyncFunction
ipcMain.handle(key, (event, ...params) => handler.val(...params));
}
}
};
// 渲染进程定义(注册) ipcMain.handle方法
export const registerHandlerForPreload = () => {
const ipcMainHandlers = getIcpMainHandler();
return Object.values(ipcMainHandlers);
};
// 以上两个方法的汇总
export const registerHandlerForMainAndPreload = () => {
registerHandlerForIcpMain();
ipcMain.on("getIcpHandler", (event, data) => {
const res = registerHandlerForPreload();
event.returnValue = JSON.stringify(res);
});
};
在 preload.js , 也需要做一些优化
const { contextBridge, ipcRenderer } = require("electron");
if (!window.electronApi) {
// 获取主进程里的ipcMainHandlers
let ipcMainHandlers = ipcRenderer.sendSync("getIcpHandler");
ipcMainHandlers = JSON.parse(ipcMainHandlers);
// 注入到渲染进程的electronApi对象中
const electronAPIContent = {
// onUsbChange: (callback) =>
// ipcRenderer.on("usbChange", (_event, value) => callback(value)),
};
ipcMainHandlers.forEach((handler) => {
if (handler.type.indexOf("Function")>-1) { // Function or AsyncFunction
electronAPIContent[handler.key] = function () {
// @ts-ignore
return ipcRenderer.invoke(handler.key, ...arguments);
};
} else {
electronAPIContent[handler.key] = handler.val;
}
});
contextBridge.exposeInMainWorld("electronApi", electronAPIContent);
}
好处
后续直接在handler文件夹中直接定义方法就行,会自动注入到渲染进程中。
既保证了安全,写起来又方便。
electron中定义ipc的完美方案的更多相关文章
- ZZmsvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义 .的分析解决办法 (转)
很久没有写程式设计入门知识的相关文章了,这篇文章要来谈谈程式库 (Library) 连结,以及关于 MSVC 与 CRT 之间的种种恩怨情仇. 如果你使用的作业系统是 Linux.Mac 或其他非 W ...
- 自定义元素 – 在 HTML 中定义新元素
本文翻译自 Custom Elements: defining new elements in HTML,在保证技术要点表达准确的前提下,行文风格有少量改编和瞎搞. 原译文地址 本文目录 引言 用时髦 ...
- Android消息推送完美方案[转]
转自 Android消息推送完美方案 推送功能在手机应用开发中越来越重要,已经成为手机开发的必须.在Android应用开发中,由于众所周知的原因,Android消息推送我们不得不大费周折.本文就是用来 ...
- Android消息推送完美方案
转自:http://bbs.hiapk.com/thread-4652657-1-1.html 推送功能在手机应用开发中越来越重要,已经成为手机开发的必须.在Android应用开发中,由于众所周知的原 ...
- 在 slua 中使用更新的面向对象方案
上一篇记录了我使用 Slua.Class 来实现面向对象扩展 C# 中得类,但实际使用中,更多地情况是直接在 lua 中定义基类然后扩展,于是触发了我重新思考下是否两种形式应该统一用一种,目前的方案中 ...
- Android中的IPC机制
Android IPC简介 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程 ...
- Java中定义常量方法及建议(Class/Interface)
Class定义常量方法(推荐方法) //final修饰符 public final class Constants { //私有构造方法 private Constants() {} public s ...
- WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了
原文:WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4 ...
- ES6 class类中定义私有变量
ES6 class类中定义私有变量 class类的不足 看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离.但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的 ...
- Maven 官网 查找&下载 jar包& pom引用 完美方案
Maven 官网 查找&下载 jar包 & pom引用 问题描述 在我们在开发过程中,经常遇到程序中需要引用的某个版本jar包,但是在公司的私有仓库下载不到的情况. 遇到这种情况,该怎 ...
随机推荐
- DevOps工程师技能
技术背景 DevOps工程师必须持有计算机科学.工程或其他相关领域的学位.2年以上工作经验.这包括开发人员.系统管理员或devops驱动的团队成员的工作.这是一个重要的需求,同时也是对所有IT操作的理 ...
- Java+Appium+Junit实现app自动化demo
1.新建maven工程和引入库 步骤参考https://www.cnblogs.com/wanyuan/p/16408758.html 2.编写代码 代码如下: import org.junit.Af ...
- eolinker环境变量配置:用例执行前给把某参数设置为全局参数的方法
特别注意:需要使用全局变量或者预处理前务必阅读本链接https://www.cnblogs.com/becks/p/13713278.html 1.场景分析 注册会员流程共计有添加数据,校验数据,提交 ...
- Linux系统中的软件管理
简介 Linux 系统中的软件管理体系主要包括软件包管理工具.软件仓库以及相关的依赖管理等方面.以下是详细介绍: 软件包管理工具 dpkg:Debian 及其衍生系统(如 Ubuntu)使用的底层软件 ...
- git 更新和强制更新失败
Your local changes to the following files would be overwritten by mergeerror: Your local changes to ...
- 5.3K star!硅基生命新纪元,这个开源数字人框架要火!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "只需3分钟视频素材,就能打造专属数字分身!""开源免费商用, ...
- 炸裂!!!Deepseek接入个人知识库,回答速度飞起来,确实可以封神了
高效管理知识.快速获取信息成为提升工作效率的关键.无论是做技术的同学还是普通的上班族,在日常积累了大量的知识数据和内容.项目文档.会议记录到技术手册.业务流程,这些信息如同宝藏一般,等待着被高效利用. ...
- WO Mic - 免费麦克风
WO Mic可以将您的手机变成电脑麦克风.您无需支付一分钱购买任何设备.如果您选择无线传输,它也是便携的.数百万用户已经安装并每天都在使用它进行通话.录音.语音遥控等活动. 三大组件协同工作以实现这一 ...
- Android去掉默认的标题栏
去掉默认的标题栏:在onCreate方法里添加supportRequestWindowFeature(Window.FEATURE_NO_TITLE); @Override protected voi ...
- 【记录】Excel 2021|(三)VBA使用Selenium自动登录网页
文章目录 1 安装 Selenium Basic 2 下载webdriver 3 自动登录 1 安装 Selenium Basic 首先需要安装Selenium Basic,才能在工具栏中找到Sele ...