Electron的基础知识
系列文章
- 什么是Electron
- 如何使用Electron
- Electron的基础知识【当前文章】
- 在Electron中使用Vue
- 关于Electron性能的讨论
- 在Electron中使用C++扩展
三、Electron的基础知识
如同打仗,赢得了每一场战役,却输掉了整场战争,是很悲剧的事情。软件开发也是如此。有时候,我们做好了每一件事,却研发出了一个很糟糕的产品。往往是架构设计出现了问题。学习一套框架更是如此,我们不仅要了解这个框架的技术细节,正要看到它的整体面貌。细节我们可以在日常使用中一点一点积累,整体架构要一开始就夯实。不然,地基不稳,很多细节的理解很容易出现偏差。最终落个知其言而不知其所以然的混沌状态。现在,我们现在开始从Electron的架构设计入手来看看它。
Electron是基于Chromium和Node.js的,熟悉他们的都知道,二者都是多进程架构的。Electron也一样。它分为主进程和渲染进程。其中,主进程只有一个,渲染进程可以有很多个。然后,还有多个工作者进程。其基本架构为:
事实上,这就是Chromium浏览器的架构。其中,每个渲染进程都是一个窗口,而主进程负责管理所有的窗口及其对应的渲染进程,同时处理网页窗口外的部分界面,如开发工具、窗口菜单等等。我们知道,正常我们在网页中是一个受限的环境,为了安全,浏览器把网页锁在了一个“笼子”里面。所以,我们浏览网页的时候不会因为恶意网页而损坏我们电脑中的数据。在浏览网页时是合理的。但是,对于桌面开发,这就成了一个问题。会导致很多功能无法实现。所以,Electron内置了很多功能模块来突破浏览器的限制。其中,大部分模块被放在了主进程里面。渲染进程要想使用这些功能,就必须跟主进程进行通信。所以,主进程与渲染进程的互访是一个很重要的话题。
3.1 主进程和渲染进程的划分
以咱们写的那个简单的demo为例,其中包含三个文件,分别是package.json
、index.js
和index.html
。其中,
-
package.json
是配置文件,用于记录依赖包、编译等配置的。 -
index.js
是主进程运行的脚本。 -
index.html
是渲染进程运行的网页。
所以,我们可以看到在index.js
中会创建一个窗口并加载index.html
。实际上,如果index.html
页面被关闭了,其窗口和进程也是由index.js
运行的主进程回收的。渲染进程只需要负责显示界面、接收用户输入、响应用户的操作就可以了。
在demo中,我们使用的是静态网页,所以,并没有给渲染进程开启访问Node.js的能力。实际上,如果我们创建窗口时将nodeIntergration
设置为true
之后,渲染进程就可以使用Node.js了。
3.2 主进程与渲染进程之间的通信
基于demo2,在demo3
中,加了一点点功能。就是在我们让渲染进程启动后立即发送了一个消息给主进程。然后,主进程再回复一个消息给渲染进程。接着,渲染进程收到主进程发过来的消息后,将消息内容显示到页面上面。
index.js
的代码如下:
// index.js
const { app, BrowserWindow, ipcMain } = require("electron");
ipcMain.on("msg2main", (event, msg) => {
console.log("Received a msg in main: ", msg)
event.sender.send("msg2render", "this is a message from main");
})
app.whenReady().then(() => {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
})
win.loadFile("./index.html");
setTimeout(() => {
console.log("send a msg in main");
win.webContents.send("msg2render", "this is a message from main, delay one second");
}, 1000.0);
})
相对于demo2
,
首先,咱们引入了一个名为ipcMain
的模块。这个模块实现了主进程的ipc功能。我们用它可以接收渲染进程发送过来的消息。如第4~7行所演示的,我们可以接收一个名为msg2main
的消息,并发送一个回复消息给渲染进程。需要注意的是,默认通信是异步的。这是因为,javascript是单线程执行的。如果大量使用同步函数,很容易造成卡顿甚至卡死。这也是Node.js提供异步I/O的核心原因。我们当然可以在主进程的监听函数中直接返回回复,但是,极度不推荐这样做。这会带来很大的性能问题。这儿就不演示同步通信了。
其中,第6行,还可以写成:
event.reply("msg2render", "this is a message from main");
这两种写法是完全一模一样的。只是不同的接口封装而已。
如果主进程想主动发送消息给渲染进程,那就需要使用BrowserWindow
类中webContents
的send
函数了。第17~20行演示的就是这种。
为什么渲染进程发消息给主进程直接使用ipcRenderer
就可以了,而主进程给渲染进程不能使用ipcMain
呢?答案很简单,因为渲染进程有多个,主进程只有一个。如果设计成使用ipcMain
给渲染进程发消息,那就需要先找到是哪个窗口。不如索性把发送函数放在这个窗口的类里面不好么。
index.html
的代码如下:
<!DOCTYPE html>
<html>
<head>
<title>demo</title>
</head>
<body>
<span id="msg"></span>!
<script lang="javascript">
let {ipcRenderer} = require("electron");
ipcRenderer.on("msg2render", (event, msg)=>{
console.log("Received a msg in render: ", msg)
document.querySelector("#msg").innerText += "\n"+ msg;
});
ipcRenderer.send("msg2main", "this is a message from render");
</script>
</body>
</html>
相对于demo2
,主要多了第8~17行的脚本。
首先,从electron
中引入ipcRenderer
模块。
然后,给ipcRenderer
的msg2render
添加一个监听函数,在这个函数中,我们把消息内容打印到控制台并显示到页面中。
最后,通过ipcRenderer
模块发送消息给主进程。这样,主进程就会发回复回来,完成整个通信交互。
3.3 主进程和渲染进程可以做什么
我们说过,为了实现桌面应用程序的开发,Electron内置了很多功能模块。这些模块的具体接口可以在Electron官网提供的API文档中查到。简单列举如下:
模块名 | 归属 | 简介 |
---|---|---|
app | 主进程 | 用于控制应用程序的事件生命周期。 |
BrowserWindow | 主进程 | 用于创建和控制浏览器窗口。 |
clipboard | 主进程、渲染进程 | 在系统剪贴板上执行复制和粘贴操作。 |
contentTracing | 主进程 | 从Chromium收集追踪数据以找到性能瓶颈和慢操作。 |
crashReporter | 主进程、渲染进程 | 用于将崩溃日志提交给远程服务器 |
desktopCapturer | 主进程 | 用来从桌面捕获音频和视频的媒体源的信息。 |
dialog | 主进程 | 用于显示用于打开和保存文件、警报等的本机系统对话框。 |
globalShortcut | 主进程 | 在应用程序没有键盘焦点时,监听键盘事件。 |
ipcMain | 主进程 | 从主进程到渲染进程的异步通信。 |
Menu | 主进程 | 创建原生应用菜单和上下文菜单。 |
MessageChannelMain | 主进程 | 主进程中用于通道消息传递的通道接口。 |
MessagePortMain | 主进程 | 主进程中用于通道消息传递的端口接口。 |
nativeImage | 主进程、渲染进程 | 使用 PNG 或 JPG 文件创建托盘、dock和应用程序图标。 |
nativeTheme | 主进程 | 读取并响应Chromium本地色彩主题中的变化。 |
net | 主进程 | 使用Chromium的原生网络库发出HTTP / HTTPS请求 |
Notification | 主进程 | 创建OS(操作系统)桌面通知 |
parentPort | 通用 | 与父进程通信接口。 |
powerMonitor | 主进程 | 监视电源状态的改变。 |
powerSaveBlocker | 主进程 | 阻止系统进入低功耗 (休眠) 模式。 |
process | 主进程、渲染进程 | 当前程序进程的相关信息 |
protocol | 主进程 | 注册自定义协议并拦截基于现有协议的请求。 |
safeStorage | 主进程 | 允许访问简单的加密和解密字符串,以便存储在本地机器上。 |
screen | 主进程 | 检索有关屏幕大小、显示器、光标位置等的信息。 |
session | 主进程 | 管理浏览器会话、cookie、缓存、代理设置等。 |
shell | 主进程、渲染进程 | 使用默认应用程序管理文件和 url。 |
Tray | 主进程 | 添加图标和上下文菜单到系统通知区 |
utilityProcess | 主进程 | 用于创建子进程。与Node.js提供的child_process.fork相似, 只是它使用的是Chromium的API |
webContents | 主进程 | 渲染以及控制 web 页面 |
contextBridge | 渲染进程 | 在隔离的上下文中创建一个安全的、双向的、同步的桥梁。 |
ipcRenderer | 渲染进程 | 从渲染器进程到主进程的异步通信。 |
webFrame | 渲染进程 | 用于调整当前页面的一些属性,如缩放倍数、拼写检查等等 |
webUtils | 渲染进程 | 主要用于获取网页File对象的真实完整路径的。 |
系列文章
- 什么是Electron
- 如何使用Electron
- Electron的基础知识【当前文章】
- 在Electron中使用Vue
- 关于Electron性能的讨论
- 在Electron中使用C++扩展