前语

发现在主进程和渲染进程通信的设计中,很多代码都是重复的,导致最后非常臃肿,且不利于后期扩展

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的完美方案的更多相关文章

  1. ZZmsvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义 .的分析解决办法 (转)

    很久没有写程式设计入门知识的相关文章了,这篇文章要来谈谈程式库 (Library) 连结,以及关于 MSVC 与 CRT 之间的种种恩怨情仇. 如果你使用的作业系统是 Linux.Mac 或其他非 W ...

  2. 自定义元素 – 在 HTML 中定义新元素

    本文翻译自 Custom Elements: defining new elements in HTML,在保证技术要点表达准确的前提下,行文风格有少量改编和瞎搞. 原译文地址 本文目录 引言 用时髦 ...

  3. Android消息推送完美方案[转]

    转自 Android消息推送完美方案 推送功能在手机应用开发中越来越重要,已经成为手机开发的必须.在Android应用开发中,由于众所周知的原因,Android消息推送我们不得不大费周折.本文就是用来 ...

  4. Android消息推送完美方案

    转自:http://bbs.hiapk.com/thread-4652657-1-1.html 推送功能在手机应用开发中越来越重要,已经成为手机开发的必须.在Android应用开发中,由于众所周知的原 ...

  5. 在 slua 中使用更新的面向对象方案

    上一篇记录了我使用 Slua.Class 来实现面向对象扩展 C# 中得类,但实际使用中,更多地情况是直接在 lua 中定义基类然后扩展,于是触发了我重新思考下是否两种形式应该统一用一种,目前的方案中 ...

  6. Android中的IPC机制

    Android IPC简介 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程 ...

  7. Java中定义常量方法及建议(Class/Interface)

    Class定义常量方法(推荐方法) //final修饰符 public final class Constants { //私有构造方法 private Constants() {} public s ...

  8. WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了

    原文:WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4 ...

  9. ES6 class类中定义私有变量

    ES6 class类中定义私有变量 class类的不足 看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离.但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的 ...

  10. Maven 官网 查找&下载 jar包& pom引用 完美方案

    Maven 官网 查找&下载 jar包 & pom引用 问题描述 在我们在开发过程中,经常遇到程序中需要引用的某个版本jar包,但是在公司的私有仓库下载不到的情况. 遇到这种情况,该怎 ...

随机推荐

  1. DevOps工程师技能

    技术背景 DevOps工程师必须持有计算机科学.工程或其他相关领域的学位.2年以上工作经验.这包括开发人员.系统管理员或devops驱动的团队成员的工作.这是一个重要的需求,同时也是对所有IT操作的理 ...

  2. Java+Appium+Junit实现app自动化demo

    1.新建maven工程和引入库 步骤参考https://www.cnblogs.com/wanyuan/p/16408758.html 2.编写代码 代码如下: import org.junit.Af ...

  3. eolinker环境变量配置:用例执行前给把某参数设置为全局参数的方法

    特别注意:需要使用全局变量或者预处理前务必阅读本链接https://www.cnblogs.com/becks/p/13713278.html 1.场景分析 注册会员流程共计有添加数据,校验数据,提交 ...

  4. Linux系统中的软件管理

    简介 Linux 系统中的软件管理体系主要包括软件包管理工具.软件仓库以及相关的依赖管理等方面.以下是详细介绍: 软件包管理工具 dpkg:Debian 及其衍生系统(如 Ubuntu)使用的底层软件 ...

  5. git 更新和强制更新失败

    Your local changes to the following files would be overwritten by mergeerror: Your local changes to ...

  6. 5.3K star!硅基生命新纪元,这个开源数字人框架要火!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "只需3分钟视频素材,就能打造专属数字分身!""开源免费商用, ...

  7. 炸裂!!!Deepseek接入个人知识库,回答速度飞起来,确实可以封神了

    高效管理知识.快速获取信息成为提升工作效率的关键.无论是做技术的同学还是普通的上班族,在日常积累了大量的知识数据和内容.项目文档.会议记录到技术手册.业务流程,这些信息如同宝藏一般,等待着被高效利用. ...

  8. WO Mic - 免费麦克风

    WO Mic可以将您的手机变成电脑麦克风.您无需支付一分钱购买任何设备.如果您选择无线传输,它也是便携的.数百万用户已经安装并每天都在使用它进行通话.录音.语音遥控等活动. 三大组件协同工作以实现这一 ...

  9. Android去掉默认的标题栏

    去掉默认的标题栏:在onCreate方法里添加supportRequestWindowFeature(Window.FEATURE_NO_TITLE); @Override protected voi ...

  10. 【记录】Excel 2021|(三)VBA使用Selenium自动登录网页

    文章目录 1 安装 Selenium Basic 2 下载webdriver 3 自动登录 1 安装 Selenium Basic 首先需要安装Selenium Basic,才能在工具栏中找到Sele ...