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包,但是在公司的私有仓库下载不到的情况. 遇到这种情况,该怎 ...
随机推荐
- Hello, PostgreSQL
Hello, PostgreSQL Whoami:5年+金融.政府.医疗领域工作经验的DBA Certificate:PGCM.OCP.YCP Skill:Oracle.Mysql.PostgreSQ ...
- Kylin-Server-V10-SP3物理机安装简要过程
1.下载镜像 链接: https://eco.kylinos.cn/partners/mirror.html?class_id=1&query_key=V10 选择: 银河麒麟高级服务器操作系 ...
- JVM 新生代垃圾回收如何避免全堆扫描?
JVM 新生代垃圾回收如何避免全堆扫描? 在 JVM 新生代的垃圾回收(Minor GC)过程中,为了提高效率并减少回收时间,垃圾收集器会避免对整个堆(包括新生代和老年代)进行扫描.以下是 JVM 如 ...
- 一文速通Python并行计算:09 Python多进程编程-进程之间的数据同步-基于互斥锁、递归锁、信号量、条件变量、事件和屏障
一文速通 Python 并行计算:09 Python 多进程编程-进程之间的数据同步-基于互斥锁.递归锁.信号量.条件变量.事件和屏障 摘要: 多进程同步机制包括互斥锁.递归锁.信号量.条件变量.事件 ...
- K8s容器运行时,移除Dockershim后存在哪些疑惑?
K8s容器运行时,移除Dockershim后存在哪些疑惑? 大家好,我是秋意零. K8s版本截止目前(24/09)已经发布到了1.31.x版本.早在K8s版本从1.24.x起(22/05),默认的容器 ...
- 17.8K star!完美超越宝塔的产品,像呼吸一样部署应用,这款开源神器绝了!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 Dokploy是一个强大的开源平台,旨在简化全栈 Web 应用的开发和部署.通过其直观的界面 ...
- idea格式化代码快捷键
Ctrl+Alt+L Ctrl+Shift+Alt+L
- Mybatis 框架课程第一天
目录 1 框架概述 1.1 MyBatis 框架概述 1.2 JDBC 编程的分析 1.2.1 jdbc 程序的回顾 1.2.2 jdbc问题分析 2 Mybatis框架快速入门 2.1 Mybati ...
- JDK安装及IDE安装编辑
1.下载及安装JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 环境变量的系统变量填上如下: 变量 ...
- Django Formsets总结
formset是将多个表单用在同一个页面上的抽象层. 我们有: from django import forms class ArticleForm(forms.Form): title=forms. ...