初识Electron 进程通信

news2024/11/7 9:49:11

概述

Electron = chromium + nodejs + native API,也就是将node环境和浏览器环境整合到了一起,这样就构成了桌面端(chromium负责渲染、node负责操作系统API等)

流程模型

预加载脚本:运行在浏览器环境下,但是也能访问部分node API
在这里插入图片描述

初始化项目

先创建一个vue3+vite的项目,然后在项目当中安装electron。具体命令就是这样

npm create vite@latest vue-electron
cd vue-electron
npm i
npm i electron -D

构建Electron项目

在package.json文件当中修改和添加type和main配置,electron脚本运行在node环境下,所以type要设置为commonjs,然后main指向electron运行的入口文件,再将启动命令加进去,这样就可以通过 npm run start 来启动electron座面应用了

  "type": "commonjs",
  "main": "electron/main/index.js"

  // 启动命令
  "start": "electron .",

编写electron/main/index.js。通过BrowserWindow创建一个浏览器窗口,然后可以通过loadURL加载一个网页,或者通过loadFile加载一个html文件进行渲染。然后还可以通过初始化监听去创建对应的窗口和关闭窗口的时候去退出electron。

const { app, BrowserWindow } = require('electron')
const createWindow = () => {
    const win = new BrowserWindow({
        width: 800,
        height: 600
    })

    win.loadURL('http://localhost:5173/');
    // win.loadFile('index.html')

}
app.whenReady().then(() => {
    createWindow()
    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})
app.on("window-all-closed", () => {
  console.log("在关闭一个应用的所有窗口后让它退出");
  // 在关闭一个应用的所有窗口后让它退出
  if (process.platform === "darwin") {
    app.quit();
  }
});

窗口常用方法

在win【BrowserWindow】对象当中会有一些常用的方法,这里简单列出来一下,完整的在这BrowserWindow Object API

  • getAllWindows:所有打开的窗口的数组
  • getFocusedWindow:此应用程序中当前获得焦点的窗口
  • fromBrowserView:通过给定的视图返回对应的窗口
  • close:关闭窗口
  • maximize:最大化窗口
  • unmaximize:取消窗口最大化
  • 还有最小化、全屏、聚焦、背景、阴影、透明等等,用的时候可以再去文档里面找一下对应的方法调用

命令合并

在这里是vue3和electron项目都在一起,并且是直接访问5137端口加到桌面端的,这个时候可以通过 npm-run-all 这个包可以将命令进行合并。先npm install npm-run-all这个包,然后改一下下面的启动脚本,通过 npm run deva 就可以直接启动vue3和electron项目了。

{
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",
    "start": "electron .",
    "deva": "npm-run-all --parallel dev start"
  },
}

预加载脚本

预加载脚本通过进行配置,在electron入口文件中进行配置,这里配置了一个同目录下的preload作为先进行加载的js脚本。

其中对于 WebPreferences Object 对象的配置可以看这个官方文档的配置,其中有几个比较常用的

  • devTools boolean 是否开启 DevTools
  • preload string 预加载的脚本文件地址
  • nodeIntegration boolean 是否启用Node integration 是否可以使用 Node.js 功能的配置
  • contextIsolation boolean 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本. 默认为 true
    • nodeIntegration设置为true后require is not define。需要设置contextIsolation为false
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

扩展

这里来一个简单的electron当中的预加载脚本看一下并且进行扩展下

contextBridge API 上下文桥梁,其他更多详细的配置可以参考官方文档,下面只说一下使用到的

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

exposeInMainWorld方法

contextBridge.exposeInMainWorld(apiKey, api)

  • apiKey string - 将 API 注入到 窗口 的键。 API 将可通过 window[apiKey] 访问。
  • api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。
const { contextBridge } = require('electron')
/**
 * 在预处理的过程当中,不能直接将值给绑定到window对象上再取值,
 * 虽然预加载脚本与其所附着的渲染器在共享着一个全局 window 对象,
 * 但并不能从中直接附加任何变动到 window 之上,因为 contextIsolation 是默认的。
 * 所以在preload当中window.myAPI = { desktop: true } 设置了myAPI,但是在render当中获取不到console.log(window.myAPI)
 */
contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
})

因为这个脚本是预加载的,在项目启动之后就会执行,并且他里面的数据都是直接绑定在window对象上,那么在这个项目里面其他的地方就可以直接使用了,比方说在vue代码当中使用:

这里不能通过模版插值直接写的,原因很简单,在vue里面只会去加载setup里面的数据,但是versions是window的,所以要在js里面用

<script setup lang="ts">
import {onMounted} from 'vue';

onMounted(() => {
  const information = document.getElementById('info') as HTMLDivElement;
  information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`;
});
</script>

<template>
  <div id="info"></div>
  <div>versions: {{ versions }}</div>
</template>

进程通信

概述

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

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

Electron 应用程序将控制两种类型的进程:主进程和渲染器进程。

主进程&渲染进程是啥

主进程 ipcMain

  • 是应用的核心,在应用启动时运行,并在整个应用的生命周期中保持活动状态
  • 主要功能
    • 创建和管理应用程序窗口
    • 管理窗口生命周期
    • 使用原生API

渲染进程

  • 是应用的用户界面部分,渲染线程运行在 Chromium 内核中,可以像 Web 浏览器一样加载和呈现网页
  • 主要功能
    • 一个应用可以有多个渲染进程
    • Windows 中展示的界面通过渲染进程表现
    • 每个窗口都是一个独立的渲染线程
    • 无法直接操作原生API

渲染进程到主进程(单向通信)

在这里借用预加载脚本进行通信,这里演示一下直接以vue3页面作为渲染进程,然后通过preload预加载脚本传递到electron入口也就是主进程。

<script setup lang="ts">
const renderToMainProcess = () => {
  electronApi.setTitle('渲染进程');
};
</script>

然后这个方法是在预加载脚本preload.js通过exposeInMainWorld进行绑定在window对象上的,所以在上面可以直接使用

contextBridge.exposeInMainWorld('electronApi', {
  // 渲染器进程到主进程的通信(单向通信)
  setTitle: (title) => ipcRenderer.send('set-title', title)
});

在最后可以在electron入口当中拿到这个数据。这里就有问题了,这是怎么知道触发了setTitle方法?

ipcMain:API在这

ipcRenderer:是一个 EventEmitter 的实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。

在ipcMain当中可以通过on、once等方法来监听ipcRenderer发送的事件。这就类比一个bus事件总线,从ipcRenderer发的set-title事件之后,从ipcMain当中拦截监听这个事件,并且指定对应的回调来处理后续操作。

  • event:他是一个 IpcMainEvent 对象。在这个可以直接通过点去获取里面的属性,其中sender WebContent - 返回发送消息的 webContents
  • 然后通过 fromWebContents() 方法找到对应的窗口对象,最后通过setTitle设置标题
  • 补充:直接用win.setTitle其实也是一样的效果,这是因为这个渲染进程和主进程是在同一个页面上的,所以效果其实是一样的。因为 win 对象是在创建窗口时定义的,它代表了整个窗口,而不是特定的渲染进程。而我们需要的是找到发送消息的特定渲染进程对应的窗口对象,
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  ipcMain.on('set-title', (event, title) => {
    const webContents = event.sender;
    const ipcWin = BrowserWindow.fromWebContents(webContents);
    ipcWin.setTitle(title);
    // win.setTitle(title)
  });

  win.loadURL('http://localhost:5173/');
};

渲染进程到主进程(双向通信)

在主进程(入口)当中添加以下代码:这里模拟的效果是在vue当中点击按钮后通过electron主进程打开文件系统选择文件,选择文件之后拿到文件的路径之后再传递给渲染进程(vue)进行显示。

dialog API在这

  • 显示用于打开和保存文件、警报等的本机系统对话框
  • showOpenDialog打开这是一个异步的所以给他加了async和await变成同步,他对应的也有一个showOpenDialogSync同步方法。
  • Promise成功包含的数据
    • canceled boolean - 对话框是否被取消。
    • filePaths string[] - 用户选择的文件路径的数组. 如果对话框被取消,这将是一个空的数组。因为前面调用showOpenDialog方法的时候没有给他设置multiSelections参数(文件多选),这里直接取数组下标0也就是文件的路径了。
  • ipcMain.handle:为一个invokeable的IPC 添加一个handler。 每当一个渲染进程调用 ipcRenderer.invoke(channel, ...args) 时这个处理器就会被调用。
async function handleFileOpen () {
  const { canceled, filePaths } = await dialog.showOpenDialog({})
  if (!canceled) {
    return filePaths[0]
  }
}

app.whenReady().then(() => {
  ipcMain.handle('dialog:openFile', handleFileOpen)
  createWindow();
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

在预加载脚本当中把dialog:openFile给定义下。这里用的是invoke:表示通过 channel 向主过程发送消息,并异步等待结果。拿到这一步就实现了主进程和渲染进程的双向通信机制,和单向通信进行对比的区别在于单向通信使用到的send和on是单向传递(理解成on和emit就可以了,只能单向传递)。而双向通信通过invoke和handle就像是一个监听和异步等待的效果,每一个事件都会在ipcRenderer当中异步等待响应结果,得到结果之后直接响应给渲染进程也就完成了双向通信

contextBridge.exposeInMainWorld('electronApi', {
  // 渲染器进程到主进程的通信(双向通信)
  openFile: () => ipcRenderer.invoke('dialog:openFile')
});

最后就是在vue渲染进程当中进行触发

<script setup lang="ts">
import {ref} from 'vue';

const renderToMainSingleProcess = () => {
  electronApi.setTitle('渲染进程');
};

let filePath = ref('')
const renderToMainMultiProcess = async () => {
  filePath.value = await window.electronApi.openFile()
};
</script>

<template>
  <div>渲染进程->主进程通信</div>
  <el-button type="primary" @click="renderToMainSingleProcess">单向</el-button>
  <el-button type="primary" @click="renderToMainMultiProcess">双向</el-button>
  <div>双向通信的文件路径为:{{ filePath }}</div>
</template>

主进程到渲染进程

在electron主进程当中添加一个menu菜单项,这里通过webContents.send发送消息

  const menu = Menu.buildFromTemplate([
    {
      label: app.name,
      submenu: [
        {
          click: () => win.webContents.send('update-counter', 1),
          label: '加一'
        },
        {
          click: () => win.webContents.send('update-counter', -1),
          label: '减一'
        }
      ]
    }
  ]);

  Menu.setApplicationMenu(menu);

预加载脚本暴露

contextBridge.exposeInMainWorld('electronApi', {
  // 主进程向渲染器进程发送消息
  onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
  counterValue: (value) => ipcRenderer.send('counter-value', value)
});

最后在渲染进程当中调用即可实现效果,在这里的onUpdateCounter相对于一个回调触发,在主进程当中调用update-counter之后会通过预加载暴露的方法到渲染进程,这个渲染进程可以理解成一个监听器,得到监听结果之后就可以拿到改变的值了。

补充:然后这里的渲染进程和主进程的通信看前面的就好了,也就是预加载脚本通过send发送,在主进程当中通过ipcMain.on得到发送的消息

<script setup lang="ts">
import {ref} from 'vue';

let counter = ref(0);
const mainToRender = () => {
  window.electronApi.onUpdateCounter((value) => {
    const oldValue = Number(counter.value);
    const newValue = oldValue + value;
    counter.value = newValue.toString();
    window.electronApi.counterValue(newValue);
  });
};
</script>

<template>
  <el-button type="primary" @click="mainToRender">主进程->渲染进程通信</el-button>
  <div>Current value = {{ counter }}</div>
</template>

遇见的问题

问题一:-32601

开启了控制台报错:win.webContents.openDevTools();

{"code":-32601,"message":"'Autofill.enable' wasn't found"}

异常信息:在打开了控制台的时候会报这个错误,但是并不影响实际使用:这个持续可以看一下Electron GitHub issues

问题二:Authors is required
An unhandled rejection has occurred inside Forge:
Error: Failed with exit code: 1
Output:
���ڳ��Դӡ�web_electron.nuspec�����ɳ������
Authors is required.
Description is required.

可以看到这个的报错是Authors和Description都是必填的,这个在package.json文件当中可能在初始化项目时没有填这两项,这两项是必填的,补充一下即可

打包之后在out目录下会有改exe文件,exe文件名对应的是项目名称,out\web-electron-win32-x64到这里也就完成了electron的打包了,这个exe文件就可以直接在电脑上打开了。

打包

electron-build打包

这里安装electron-builder,需要安装在开发环境当中

npm i electron-builder -D

而后在package.json里面添加build配置,这里简化些,下面是需要添加的打包配置。打包后在dist目录下的exe直接安装即可

{
  "scripts": {
    "build-win": "electron-builder"
  },
  "build": {
    "productName": "Electron",
    "appId": "com.modify",
    "copyright": "by modify",
    "asar": true,
    "win": {
      "icon": "electron/logo.ico"
    },
    "nsis": {
      "oneClick": false,
      "allowElevation": true,
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "electron/logo.ico",
      "uninstallerIcon": "electron/logo.ico",
      "installerHeaderIcon": "electron/logo.ico",
      "createDesktopShortcut": true,
      "createStartMenuShortcut": true,
      "shortcutName": "APP",
      "runAfterFinish": true
    }
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小菜家教平台(三):基于SpringBoot+Vue打造一站式学习管理系统

目录 前言 今日进度 详细过程 相关知识点 前言 昨天重构了数据库并实现了登录功能&#xff0c;今天继续进行开发&#xff0c;创作不易&#xff0c;请多多支持~ 今日进度 添加过滤器、实现登出功能、实现用户授权功能校验 详细过程 一、添加过滤器 自定义过滤器作用&…

小新学习k8s第六天之pod详解

一、资源限制 Pod是k8s中的最小的资源管理组件&#xff0c;pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。k8s中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod运行的StatefulSet和Deployment等…

戴尔电脑 Bios 如何进入?Dell Bios 进入 Bios 快捷键是什么?

BIOS&#xff08;基本输入输出系统&#xff09;是计算机启动时运行的第一个程序&#xff0c;它负责初始化硬件并加载操作系统。对于戴尔电脑用户来说&#xff0c;有时可能需要进入 BIOS 进行一些特定的设置调整&#xff0c;比如更改启动顺序、调整性能选项或解决硬件兼容性问题…

【UE5】一种老派的假反射做法,可以用于移动端,或对反射的速度、清晰度有需求的地方

没想到大家这篇文章呼声还挺高 这篇文章是对它的详细实现&#xff0c;建议在阅读本篇之前&#xff0c;先浏览一下前面的文章&#xff0c;以便更好地理解和掌握内容。 这种老派的假反射技术&#xff0c;适合用于移动端或对反射效果的速度和清晰度有较高要求的场合。该技术通过一…

MongoDB笔记02-MongoDB基本常用命令

文章目录 一、前言二、数据库操作2.1 选择和创建数据库2.2 数据库的删除 3 集合操作3.1 集合的显式创建3.2 集合的隐式创建3.3 集合的删除 四、文档基本CRUD4.1 文档的插入4.1.1 单个文档插入4.1.2 批量插入 4.2 文档的基本查询4.2.1 查询所有4.2.2 投影查询&#xff08;Projec…

【启程Golang之旅】深入理解 Protocol Buffers 及其应用

如果你是 Go 语言的开发者&#xff0c;理解如何在 Go 中使用 Protobuf&#xff0c;将帮助你大幅提升数据传输的效率&#xff0c;并实现更高性能的系统设计。 本篇文章将深入探讨 Go 语言中使用 Protobuf 的基础知识、常见应用以及最佳实践&#xff0c;带你一步步了解如何在项目…

最新发布:数智人才成长引擎

数字经济时代 数智人才缺口不断增大 根据公开发布的信息显示&#xff0c;中国&#xff0c;作为全球最大的数字经济体之一&#xff0c;当前数智人才&#xff08;涵盖了数智人才、数据分析师、AI专家、云计算工程师等关键岗位&#xff09;的总体缺口已攀升至惊人的2500万至3000…

ServletContext介绍

文章目录 1、ServletContext对象介绍1_方法介绍2_用例分析 2、ServletContainerInitializer1_整体结构2_工作原理3_使用案例 3、Spring案例源码分析1_注册DispatcherServlet2_注册配置类3_SpringServletContainerInitializer 4_总结 ServletContext 表示上下文对象&#xff0c;…

Linux的硬盘管理

硬盘有价&#xff0c;数据无价 1. 硬盘的概念 硬盘是一种计算机的存储设备&#xff0c;通常是由一个或者多个磁性盘片组成。硬盘即可以安装在计算机的内部&#xff0c;也可以外接计算机。 保存数据 数据&#xff1a;操作系统&#xff0c;应用程序&#xff0c;文档多媒体文件…

云渲染与汽车CGI图像技术优势和劣势

在数字时代&#xff0c;云渲染技术以其独特的优势在汽车CGI图像制作中占据了重要地位。云渲染通过利用云计算的分布式处理能力&#xff0c;将渲染任务分配给云端的服务器集群进行计算&#xff0c;从而实现高效、高质量的渲染效果。 这种技术的优势主要体现在以下几个方面&#…

鸿蒙NEXT开发-学生管理系统小案例(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

《潜行者2切尔诺贝利之心》游戏引擎介绍

潜行者2切尔诺贝利之心是基于虚幻5引擎&#xff0c;所以画面效果大家不必担心。游戏目前已经跳票了很久&#xff0c;预计发售时间是2024 年 11 月 21 日&#xff0c;这次应该不会再跳票。 潜行者2切尔诺贝利之心是虚幻5吗 答&#xff1a;是虚幻5。 潜行者官方推特之前回复了…

WPF+MVVM案例实战(十八)- 自定义字体图标按钮的封装与实现(ABD类)

文章目录 1、案例效果1、按钮分类2、ABD类按钮实现描述1.文件创建与代码实现2、样式引用与控件封装3、按钮案例演示1、页面实现与文件创建2、运行效果如下3、总结4、源代码获取1、案例效果 1、按钮分类 在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,…

在Vue和OpenLayers中使用移动传感器实现飞机航线飞行模拟

项目实现的核心代码 项目概述 该项目的目标是使用Vue.js作为前端框架&#xff0c;结合OpenLayers用于地图显示&#xff0c;实时获取来自手机传感器的数据&#xff08;如经纬度、高度、速度&#xff09;来模拟飞机在地图上的飞行轨迹。整体架构如下&#xff1a; Vue.js 用于构建…

C语言-详细讲解-洛谷P1075 [NOIP2012 普及组] 质因数分解

1.题目要求 2.题目解析 解题点在于如何分解质因数&#xff0c;这里介绍一下短除法。&#xff08;虽然解决这个问题可以不用短除法&#xff09; 3.代码实现 贴一下自己的代码 #include <stdio.h> #include <math.h>int main() {int n, i;scanf("%d",…

基于springboot的音乐网站的设计与实现(源码+lw+调试)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

RabbitMQ 管理平台(控制中心)的介绍

文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…

Golang | Leetcode Golang题解之第542题01矩阵

题目&#xff1a; 题解&#xff1a; type point struct{x, y int }var dirs []point{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}func updateMatrix(mat [][]int) [][]int {var m, n len(mat), len(mat[0])var res make([][]int, m)var visited make([][]bool, m)var queue []poin…

前端介绍|基础入门-html+css+js

文章目录 本课程有什么&#xff1f;前端是什么&#xff1f;1. **前端概述**2. **前端的工作职责**3. **前端技术栈**6. **前端开发工具**7. **HTML、CSS、JS的关系** 本课程有什么&#xff1f; 本套课程是零基础入门保姆级课程&#xff0c;课程主要内容包含&#xff1a; HTML…

自动驾驶---“火热的”时空联合规划

1 背景 早期的不少规划算法都是横纵分离的&#xff08;比如Apollo&#xff09;&#xff0c;先求解path之后&#xff0c;依赖path的结果再进行speed的求解。这种横纵解耦的规划方式具有以下特点&#xff1a; 相对较为简单&#xff0c;计算量通常较小&#xff0c;容易实现实时性…