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包,但是在公司的私有仓库下载不到的情况. 遇到这种情况,该怎 ...
随机推荐
- Armbian 安装与更换为国内软件源
Armbian 是为 ARM 架构的单板计算机(如树莓派.NanoPi.Orange Pi 等)提供的开源镜像系统,它基于 Debian 或 Ubuntu 系统.在使用 Armbian 进行开发.调试 ...
- 11. RabbitMQ 消息队列 Federation (Exchange 交换机和 Queue队列) + Shovel 同步的搭建配置
11. RabbitMQ 消息队列 Federation (Exchange 交换机和 Queue队列) + Shovel 同步的搭建配置 @ 目录 11. RabbitMQ 消息队列 Federat ...
- springAPI对事物支持之注解式配置
一.无Spring核心配置文件:applicationContext.xml下开启注解式事物 @Configuration @EnableTransactionManagement public cl ...
- Vue的前端项目开发环境搭建
一.本机window端:安装Node.js,其实质性功能相当于,java的maven https://nodejs.org/en/download/ 二.本机window端:检查Node.js的版本 ...
- 【深度思考】自定义日期格式,为什么@JSONField生效,@JsonFormat不生效?
1. 前言 最近在自测接口时,发现一个问题:字段类型定义的是Date,但接口返回值里却是时间戳(1744959978674), 而不是预期的2025-04-18 15:06:18. private D ...
- 🔥吐血整理 Bolt.diy 部署与应用攻略
1.前言 以前总是有很多人无代码基础的人总是在幻想,如何不要自己写代码就可以建立一个自己的创意网站呢?之前总觉得异想天开不可能,屏幕前的你是不是也是这么想的呢,没有想到,Bolt.diy帮你实现了,快 ...
- Nacos简介—2.Nacos的原理简介
大纲 1.Nacos集群模式的数据写入存储与读取问题 2.基于Distro协议在启动后的运行规则 3.基于Distro协议在处理服务实例注册时的写路由 4.由于写路由造成的数据分片以及随机读问题 5. ...
- dbeaver导入sql脚本报错的排查—— ERROR 1366 (HY000) at line
描述 在使用dbeaver进行sql脚本导入的时候报了以下的错误. C:\Users\xxxx\AppData\Roaming\DBeaverData\drivers\clients\mysql_8\ ...
- Python基础 - 序列结构
有序序列: 列表.元组.字符串 无序序列: 字典.集合 可变序列: 列表.字典.集合 不可变序列: 元组.字符串 基本涉及功能: 增.删.查.改 列表:升级版数组 特点: 支持双向索引 包含若干元素的 ...
- Vim-从放弃到入门
初识Vim Vim被称为神一样的编译器,人类历史上最好文本编辑器(^_^).学习成本很高,学习路线陡峭.下面列举一些入门的教程: 慕课网-玩转Vim 从放弃到爱不释手 新手必看 Vim实用技巧(第2版 ...