Electron工作流程(2)——进程间通信

概述

Electron 继承了来自 Chromium 的多进程架构,网页浏览器的基本架构是单个浏览器进程控制不同标签页进程,以及整个应用程序的生命周期。这样可以避免单个浏览器的无响应不会影响到整个浏览器。

Electron 应用的大致工作流程是:启动APP——主进程创建window——win加载页面(渲染进程)

Electron 应用程序的结构非常相似。 作为应用开发者,你将控制两种类型的进程:主进程和渲染器进程。

主进程(Main Process)

是应用的核心,在应用启动时运行,并在整个应用的生命周期中保持活动状态。

  • 每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。可以看做是 package.jsonmain 属性对应的文件。
  • 一个应用只会有一个主进程。
  • 只有主进程可以进行 GUI 的 API 操作,所以渲染进程要操作原生API,需要建立和主进程的通信。

主要功能:

  • 创建和管理应用程序窗口
  • 管理窗口生命周期
  • 使用原生API

渲染进程(Renderer Process)

是应用的用户界面部分,渲染线程运行在 Chromium 内核中,可以像 Web 浏览器一样加载和呈现 HTML、CSS 和 JavaScript。

  • 一个应用可以有多个渲染进程。
  • Windows 中展示的界面通过渲染进程表现。
  • 每个窗口都是一个独立的渲染线程。
  • 无法直接操作原生API。

**主要功能:**渲染页面

主进程和渲染进程的通信

1. ipcMainipcRenderer

主进程和渲染进程分别执行不同的职责,它们之间需要通过进程间通信(IPC)进行数据交换。Electron 提供了 ipcMainipcRenderer 模块来实现。

  • ipcMain:从主进程到渲染进程的异步通信。

    主要作用:

    • 在主进程使用时,处理从渲染进程(网页)发送过来的同步或者异步消息。
    • 从主进程向渲染进程发送消息。

    示例:

    1. 监听 channel, 当新消息到达,将通过 listener(event, args…) 调用 listener。

      **// channel 就是监听的 key ,是渲染进程发送消息设置的 key
      // listener 当监听到消息时的处理方法
      ipcMain.on(channel, listener)**
      
    2. 添加一次性 listener 函数。 这个 listener 只会在 channel下一次收到消息的时候被调用,之后这个监听器会被移除。

      ipcMain.once(channel, listener)

    3. 当一个渲染进程调用 ipcRenderer.invoke(channel, ...args) 时,主进程添加一个处理器处理结果。如果 listener 是一个有返回值的方法,那么 Promise 的最终结果就是远程调用的返回值。

      例如:渲染进程调用一个 invoke ,带返回值。

      async () => {const result = await ipcRenderer.invoke('my-invokable-ipc', arg1, arg2)// ...
      }
      

      主进程设置处理器,并接收方法返回值

      ipcMain.handle('my-invokable-ipc', async (event, ...args) => {const result = await somePromise(...args)return result
      })
      
  • ipcRenderer:从渲染器进程到主进程的异步通信。

    主要作用:

    • 从渲染进程(web页面)发送异步或同步的消息到主进程。
    • 接收主进程回复的消息。

    示例:

    1. 监听或取消监听 channel, 当新消息到达,将通过 listener(event, args…) 调用 listener。

      **// channel 就是监听的 key ,是渲染进程发送消息设置的 key
      // listener 当监听到消息时的处理方法
      ipcRenderer.on(channel, listener)
      // 只监听一次
      ipcRenderer.once(channel, listener)// 大概是防止内存泄漏之类的吧
      ipcRenderer.off(channel, listener)**
      
    2. 通过channel向主进程发送异步消息,可以发送任意参数。

      **// channel 就是监听的 key ,是渲染进程发送消息设置的 key
      // listener 当监听到消息时的处理方法
      ipcRenderer.send(channel, ...args)// 主进程监听发送的消息,监听到之后进行处理
      ipcMain.on(channel, listener)**
      
    3. 通过 channel 向主过程发送消息,并异步等待结果。(有返回值的方法)

      // 渲染进程
      ipcRenderer.invoke('some-name', someArgument).then((result) => {// ...
      })// 主进程
      ipcMain.handle('some-name', async (event, someArgument) => {const result = await doSomeWork(someArgument)return result
      })
      

2. nodeIntegrationcontextIsolation

从Electron 12开始,为了提高安全性,Electron建议在渲染进程中禁用nodeIntegration并启用contextIsolation。这时候实现进程间通信就需要使用preload.js脚本。

  • nodeIntegration : 是否可以使用 Node.js 功能的配置。当 nodeIntegration 设置为 true 时,渲染进程中可以使用 Node.js 的所有功能。在 Electron 应用中通常禁用 nodeIntegration ,减少渲染进程中执行恶意代码的风险。
  • contextIsolation :启用 contextIsolation 后,Electron 将为每个渲染进程创建一个独立的 JavaScript 上下文环境。

代码设置:

const createWindow = () => {// Create the browser window.const mainWindow = new BrowserWindow({width: 800,height: 600, show:false, // 是否在启动时展示frame:true, // 是否可以拖动title:"MyTest",webPreferences: { // 加载渲染进程preload: path.join(__dirname, 'preload.js'),  // 加载进程间通信contextIsolation:true, // 启用上下文隔离nodeIntegration:false // 禁用渲染进程调用 Node.js},});// and load the index.html of the app.mainWindow.loadFile(path.join(__dirname, 'index.html'));
// 监听 ready-to-show ,准备好之后才调用show,解决启动时白屏问题mainWindow.once('ready-to-show',()=>{mainWindow.show()})
};

3. preload.js

预加载脚本可以在渲染进程加载前运行,通过 contextBridge 将安全的 API 暴露给渲染进程。

contextBridge 是指在隔离的上下文中创建一个安全的、双向的、同步的桥梁。

API 调用示例:

  1. contextBridge.exposeInMainWorld(apiKey, api)

    /**
    * apiKey string - 将 API 注入到 窗口 的键。 API 将可通过 window[apiKey] 访问。
    * api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。
    */
    contextBridge.exposeInMainWorld(apiKey, api)// 调用示例
    const { contextBridge, ipcRenderer } = require('electron');
    contextBridge.exposeInMainWorld('electron',{openSubWindow:()=> ipcRenderer.send('open-sub-window')
    })
    
  2. contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)

    /**
    * worldId Integer - 要注入 API 的 world 的 ID。 0 是默认 world,999 的 world 
    被 Electron 的 contextIsolation 使用。 使用 999 would 为 preload 上下文暴露对象。我们建议使用 1000+ 来创建隔离的 world。
    * apiKey string - 将 API 注入到 window 的键。 API 将可通过 window[apiKey] 访问。
    * api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。
    */
    contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)// 调用示例:
    const { contextBridge, ipcRenderer } = require('electron')contextBridge.exposeInIsolatedWorld(1004,'electron',{doThing: () => ipcRenderer.send('do-a-thing')}
    )
    

需要调用的地方(如果是第二种方式则是在 id =1004 的world):

// 按钮点击事件document.getElementById('sub_btn').addEventListener('click',()=>{
//  渲染进程调用 preload.js 暴露的 API window.electron.openSubWindow()})

4. 主进程和渲染进程通信示例

用一个创建子窗口的例子理解一下主进程和渲染进程的通信。

  1. 在主窗口页面增加一个按钮,触发按钮后创建一个子窗口。

    在主窗口页面增加一个按钮,id = sub_btn,方便设置监听事件查找。

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8" /><title>Hello World!</title><link rel="stylesheet" href="index.css" /></head><body><h1>💖 Hello World!HIHI</h1><p>Welcome to your Electron application.</p><h2>打开一个新窗口</h2><button id="sub_btn">点击打开一个新窗口</button></body><script src="subIndex.js"></script>
    </html>
    
  2. 上面说到新版 Electron 进程间的通信都需要通过 preload.js ,在主窗口设置 preload.js 以及禁用渲染进程调用 Node.js ,开启上下文隔离(这个其实默认就是开启的,不设置也行)

    // node.js 的路径
    const path = require('node:path');const createWindow = () => {// Create the browser window.const mainWindow = new BrowserWindow({width: 800,height: 600,show:false,frame:true,title:"MyTest",webPreferences: {
    // 加载 preload.js preload: path.join(__dirname, 'preload.js'),// 设置环境隔离contextIsolation:true,
    // 设置禁用渲染进程禁止调用 node.jsnodeIntegration:false},});// and load the index.html of the app.mainWindow.loadFile(path.join(__dirname, 'index.html'));mainWindow.once('ready-to-show',()=>{mainWindow.show()})// Open the DevTools.mainWindow.webContents.openDevTools();
    };
    
    • __dirname :指向当前正在执行的脚本的路径。
    • path.join : 将多个路径联结在一起,创建一个跨平台的路径字符串。
  3. preload.js 对渲染进程暴露API

    // 导包,使用 ipcRender 是 从渲染器进程到主进程的异步通信,是一个 是一个 EventEmitter 的实例
    // 参考文档:https://www.electronjs.org/zh/docs/latest/api/ipc-renderer
    const { contextBridge, ipcRenderer } = require('electron');contextBridge.exposeInMainWorld('electron',{// 发送 channel 是 open-sub-window 的事件openSubWindow:()=> ipcRenderer.send('open-sub-window')
    })
    
  4. 渲染进程调用 API ,执行业务逻辑。(在主进程的 html 文件已经引用了 渲染进程的 script 文件)

    使用 window.key.API 方式调用,key 就是在 preload.js 里声明的 world 环境,API 就是在 preload.js 里声明的对渲染进程暴露的方法。

       document.getElementById('sub_btn').addEventListener('click',()=>{window.electron.openSubWindow()})
    
  5. 在主进程监听渲染进程发送的消息,并处理任务

    // 主进程监听渲染进程发送的 channel open-sub-window 消息。
    ipcMain.on('open-sub-window',(event)=>{// 处理逻辑if(!childWindow){createChildWindow();}else{childWindow.show()}
    })// 创建子窗口逻辑
    function createChildWindow(){childWindow = new BrowserWindow({width:400,height:400,parent:BrowserWindow.getAllWindows()[0],modal:true,frame:true,webPreferences:{preload: path.join(__dirname, 'preload.js'),contextIsolation: true,enableRemoteModule: false,}});
    // 加载子窗口页面childWindow.loadFile(path.join(__dirname, 'subIndex.html'));childWindow.on('closed',function(){childWindow = null;})
    }
    

完成以上过程,通过渲染进程和主进程间通信完成创建子窗口功能。

这系列文章是记录做游戏启动下载器的整个过程,启动下载器目前已有成品,在调试中,在这个过程中把学习路径都记录下来。由于启动下载器的页面都是H5 写,要先转战学习 vue3 的学习记录,后续的打包、签名内容后续更新。

参考文档

ipcMain: https://www.electronjs.org/zh/docs/latest/api/ipc-main

ipcRenderer: https://www.electronjs.org/zh/docs/latest/api/ipc-renderer

contextBridge: https://www.electronjs.org/zh/docs/latest/api/context-bridge

使用预加载脚本: https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-preload

进程间通信: https://www.electronjs.org/zh/docs/latest/tutorial/ipc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/388296.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GNSS形变监测系统

TH-WY1 GNSS形变监测系统采用扼流圈设计有以下几个优势&#xff1a; 高精度测量&#xff1a;扼流圈是一种高精度的传感器&#xff0c;可以提供非常精确的测量结果。这使得GNSS形变监测系统能够准确地测量结构物的形变变化。 高稳定性&#xff1a;扼流圈设计使得传感器具有良好…

第33篇 计算数据中最长的连续1的个数<三>

Q&#xff1a;如何将计算出的结果&#xff08;最长的连续1的个数&#xff09;显示在DE2-115开发板的HEX上&#xff1f; A&#xff1a;基本原理&#xff1a;DE2-115_Computer_System中的HEX并行端口作为内存映射设备连接到DE2-115开发板的七段数码管&#xff0c;每个端口都对应…

大模型提示工程(Prompt),让LLM自己优化提示词

前言 随着大家对于prompt提问的研究以及对于高质量回答的追求&#xff0c;现在有一个比较热的词叫做prompt creator。 Prompt Creator 实际上是使得 ChatGPT 更好的引导你去完善自己的提问&#xff0c;同时也完善自己的回答&#xff0c;更好地指导自己回答出更加令使用者满意…

win10桌面任务栏美化(不用软件)(任务栏应用居中,透明任务栏)

透明任务栏 1、打开设置——个性化——颜色&#xff0c;打开透明效果&#xff1b; 2、在搜索框搜索注册表编辑器&#xff1b; 3、找如下路径&#xff1a;计算机\HKEY-CURRENT-USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced&#xff1b; 4、寻找文件&a…

【TS】TypeScript类型断言:掌握类型转换的艺术

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript类型断言&#xff1a;掌握类型转换的艺术1. 引言2. 什么是类型断言&a…

链表的实现(C++版)

对于链表的学习,之前在C语言部分的时候就已经有学习过,也学会了使用C语言来打造一个链表.如今学了C 则想通过C来打造一个链表,以达到锻炼自己的目的. 1.链表的初步实现 1.节点模板的设置 template <class T> struct ListNode{ListNode <T>* _next;ListNode <T…

k8s学习--使用kubepshere部署devops项目时遇到的报错(无法找到gitee仓库)

今天在kubesphere部署devops项目&#xff0c;编辑流水线的时候&#xff0c;发现怎么也访问不到gitee仓库 报错的流水线位置 报错日志 报错原因 变量问题 因为看见了csy/sangomall&#xff0c;所以理所当然的把路径变量GITEE_ACCOUNT写成了用户名 解决方法 结果发现仓库…

可靠的图纸加密软件,七款图纸加密软件推荐

大家好啊,我是小固,今天跟大家聊聊图纸加密软件。 作为一名设计师,我深知保护自己的知识产权有多重要。曾经就因为图纸泄露,差点血本无归,那个教训可真是惨痛啊!所以我今天就给大家推荐几款靠谱的图纸加密软件,希望能帮到你们。 固信软件https://www.gooxion.com/ 首先要隆重…

Java语言程序设计——篇十一(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是CSDN&#xff0c;我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f33…

Vue 的安装与配置

今天是八月一日&#xff0c;我也开启了Vue的学习&#xff0c;希望和大家一起学编程&#xff0c;相互督促&#xff0c;相互进步&#xff01; 安装vscode 安装Node.js 官网&#xff1a;https://nodejs.org/zh-cn 下载完正常安装就行 可以winr输入cmd&#xff0c;也可以vscod…

springboot智能健康管理平台-计算机毕业设计源码57256

摘要 在当今社会&#xff0c;人们越来越重视健康饮食和健康管理。借助SpringBoot框架和MySQL数据库的支持&#xff0c;开发智能健康管理平台成为可能。该平台结合了小程序技术的便利性和SpringBoot框架的快速开发能力&#xff0c;为用户提供了便捷的健康管理解决方案。 通过智能…

【多线程】单例模式

&#x1f3c0;&#x1f3c0;&#x1f3c0;来都来了&#xff0c;不妨点个关注&#xff01; &#x1f3a7;&#x1f3a7;&#x1f3a7;博客主页&#xff1a;欢迎各位大佬! 文章目录 1. 什么是单例模式1.1 理解单例模式1.2 单例模式的特点 2. 饿汉模式3. 懒汉模式3.1 单线程下的懒…

中国人民解放军建军97周年

缅怀先烈&#xff0c;砥砺前行 付吾辈之韶华&#xff0c;耀吾辈之中华! 万里河山&#xff0c;有您心安!

Django REST Framework(十五)路由Routes

如何在Django REST framework中利用SimpleRouter和DefaultRouter来高效生成视图集的路由信息,并详细解释如何使用action装饰器为视图集中的自定义方法生成路由 1.路由的定义规则 路由称为URL(Uniform Resource Locator,统一资源定位符),也可以称为URLconf,是对可以从互联…

【Java题解】杨辉三角—力扣

&#x1f389;欢迎大家收看&#xff0c;请多多支持&#x1f339; &#x1f970;关注小哇&#xff0c;和我一起成长&#x1f680;个人主页&#x1f680; ⭐目前主更 专栏Java ⭐数据结构 ⭐已更专栏有C语言、计算机网络⭐ 题目链接&#xff1a;杨辉三角 目录&#x1f451; ⭐题…

the request was rejected because no multipart boundary was found

文章目录 1. 需求描述2. 报错信息3. 探索过程1. 使用postman 排除后端错误2. 搜索网上的解决方法3. 解决方法 1. 需求描述 想要在前端上传一个PDF 发票&#xff0c;经过后端解析PDF之后&#xff0c;将想要的值自动回填到对应的输入框中 2. 报错信息 org.apache.tomcat.util.…

2024年有哪些开放式耳机值得入手?值得关注的开放式耳机评测大赏

如今&#xff0c;开放式耳机越来越受到人们的关注。2024 年更是涌现出了众多优秀的开放式耳机产品。但在众多选择面前&#xff0c;哪一款耳机的音质更出色&#xff1f;哪一款佩戴起来更舒适&#xff1f;又有哪一款在通话质量和连接性能上表现更优异呢&#xff1f;接下来我将详细…

【Devops】CertD 完全免费、自动申请、自动部署SSL证书一站式管理工具 | 自动化HTTPS | 3个月SSL自动轮换

CertD CertD 是一个免费全自动申请和自动部署更新SSL证书的工具。 后缀D取自linux守护进程的命名风格&#xff0c;意为证书守护进程。 关键字&#xff1a;证书自动申请、证书自动更新、证书自动续期、证书自动续签 一、特性 本项目不仅支持证书申请过程自动化&#xff0c;还…

SpringMVC源码解析(二):请求执行流程

SpringMVC源码系列文章 SpringMVC源码解析(一)&#xff1a;web容器启动流程 SpringMVC源码解析(二)&#xff1a;请求执行流程 目录 前言DispatcherServlet入口一、获取HandlerExcutionChain(包括Handler)1、获取Handler1.1、通过request获取查找路径1.2、通过查找路径获取Han…

找工作很迷茫?程序员的岗位宝典来了!

随着数字化转型进展深入&#xff0c;大量数字化、智能化的岗位相继涌现。 但即使这样&#xff0c;大家依然认为&#xff0c;找到一份合适的工作实在是太&#xff01;难&#xff01;了&#xff01; 调查显示&#xff0c;技术创新和商业模式正在成为助推企业发展的两大动力。同时…