Electron 的进程

348 阅读6分钟

Electron 是由两种进程组成的,即主进程和渲染进程

  • 在 Electron 里,运行 package.json 里 main 脚本的进程被称为主进程Main Process),在主进程运行的脚本可以以创建 web 页面的形式展示 GUI
    • 主进程通过创建浏览器窗口实例来创建网页:每一个 浏览窗口 实例在其渲染过程中运行网页,当一个 BrowserWindow 实例被摧毁时,对应的渲染过程也被终止
    • 主进程管理所有网页及其对应的渲染过程
  • 由于 Electron 使用 Chromium 来展示页面,所以 Chromium 的多进程结构也被充分利用:每个 Electron 的页面都在运行着自己的进程,这样的进程我们称之为渲染进程Renderer Process):
    • 渲染进程只能管理单个的网页,在一个渲染过程中崩溃不会影响其他渲染过程
    • 渲染进程通过 IPC 与主进程通信,在网页上执行 GUI 操作;由于安全考虑和可能的资源泄漏,直接从渲染器过程中调用与本地 GUI 有关的 API 会受到限制## 2 进程 API

针对不同的进程,Electron 提供了不同的 API 以供使用,所以可以分为以下3类 API

2.1 主进程 API

2.2 渲染进程 API

  • ipcRenderer:从渲染器进程到主进程的异步通信
  • remote:在渲染进程中使用主进程模块
  • webFrame:自定义渲染当前网页

2.3 通用 API

  • desktopCapturer:通过 [navigator.mediaDevices.getUserMedia] API ,可以访问那些用于从桌面上捕获音频和视频的媒体源信息
  • clipboard:在系统剪贴板上执行复制和粘贴操作
  • crashReporter:将崩溃日志提交给远程服务器
  • nativeImage:使用 PNG 或 JPG 文件创建托盘、dock和应用程序图标
  • shell:使用默认应用程序管理文件和 url

2.4 示例

例如,要在两个进程中访问 Electron API,需要它包含的模块:

const electron = require('electron');

若要创建一个窗口,请调用 BrowserWindow 模块,它只能在主进程中使用:

const { BrowserWindow } = require('electron');
// 或
import { BrowerWindow} from 'electron';

const win = new BrowserWindow();

若要从渲染器调用主流程,请使用 IPC 模块:

// 在主进程中
const { ipcMain } = require('electron');
// 或
import { ipcMain } from 'electron';

ipcMain.handle('exper-action', (evidence, ...args) =>
  // ... 代表渲染器操作
});

// 在渲染进程中
const { ipcRenderer } = require('electron');
// 或
import { ipcRenderer } from 'electron';

ipcRender.invotrake('exper-action', ...args);

注意:由于渲染过程可能会运行不受信任的代码(特别是第三方的代码),重要的是要认真验证主要进程中提出的请求。
如果要从渲染过程中访问 Node.js API,我们需要设置节点集成首选项为 true

Electron 在主流程和渲染流程中显示对 Node.js API 及其模块的完全访问权限;例如,我们可以从根目录读取所有文件:

const fs = require('fs');
const root = fs.readdirSync('/');
console.log(root);

要使用 Node.js 模块,首先需要安装它作为依赖:

npm install --save aws-sdk

然后,在 Electron 应用程序中,引入模块:

const S3 = require('aws-sdk/clients/s3');

以上是 API 大致使用方式,接下来,就从主进程的几个关键 API 谈起。

3 任务栏、Dock或者桌面启动器自定义

因为一个应用的功能方方面面,所以我们这里主要是通用的东西,在一个操作系统当中,最明显的就是其系统自带功能,所以我们这里可以使用对应的模块,利用到系统自带功能,从而符合对应系统用户对应用的基础使用习惯。

首先就是 Windows 任务栏、Mac Dock 或者 Linux 的桌面启动器。

3.1 Windows 任务栏自定义

首先我们要明确,这里所谓的 Windows 任务栏是特指 Windows 7 之后版本的任务栏,因为一些新的特性是从 Win7 之后才出现。

首先我们来看看,在任务栏的应用图标上右键点击弹出的菜单,比如:

VS Code 的右键菜单,我们自定义的是“任务”这个部分

可以看到,右键点击任务栏中 VS Code 图标后,会弹出一个菜单,其中“任务”这个部分仅有一个命令——新窗口;我们在自己的应用当中也可以定义出这样一个任务菜单项,只需要在 主进程代码中(通常是 main.js 或者 index.js)增加(有基础代码的情况下,基础代码可以参考上一章节):

app.setUserTasks([{
  program: process.execPath,
  arguments: '--new-window',
  iconPath: process.execPath,
  iconIndex: 0,
  title: '新窗口',
  description: '创建新窗口',
}]);

这样在运行应用之后,我们右键点击其图标,就可以看到效果了:

接着是带有缩略图的工具栏,比如:

当然 WMP 现在很少人用了,不过这种工具栏还是很有参考价值的,如果我们开发一个和音视频有关的应用,可能也很想使用这种方式来提供更多的互动,那么可以增加如下内容:

mainWindow.setThumbarButtons([
  {
    tooltip: 'button1',
    icon: path.join(__dirname, 'button1.png'),
    click () { console.log('button1 clicked') }
  }, {
    tooltip: 'button2',
    icon: path.join(__dirname, 'button2.png'),
    flags: ['enabled', 'dismissonclick'],
    click () { console.log('button2 clicked.') }
  }
]);

突然发现这个方法有点问题,我直接用官方的方式居然没有按钮……上周因为截图有问题我就没有试过,本身用的也少,居然没发现这个问题……囧一个,我回头看看是不是自己哪里搞错了。

放出一个完整的桌面,大家可以看看,忽略我的MC桌面哈

另外还有我们常见的如果应用失去焦点,但突然获得活动更新了,可以考虑加入图表闪烁的功能,代码也很简单:

mainWindow.once('focus', () => mainWindow.flashFrame(false));
mainWindow.flashFrame(true);

3.2 Mac Dock 自定义

这个例子其实是官网的例子,代码如下:

const dockMenu = Menu.buildFromTemplate([
  {
    label: 'New Window',
    click () { console.log('New Window') }
  }, {
    label: 'New Window with Settings',
    submenu: [
      { label: 'Basic' },
      { label: 'Pro' }
    ]
  },
  { label: 'New Command...' }
])

app.whenReady().then(() => {
  app.dock.setMenu(dockMenu)
})

3.3 Linux 桌面启动器自定义

这个其实是围绕着 Linux 桌面的 .desktop 文件来实现的,实际上不能算是直接通过 API 编程实现的,下面是个例子:

Actions=PlayPause;Next;Previous

[Desktop Action PlayPause]
Name=Play-Pause
Exec=audacious -t


[Desktop Action Next]
Name=Next
Exec=audacious -f


[Desktop Action Previous]
Name=Previous
Exec=audacious -r


我今天检查截图才发现了自己上周的有关 ThumbarButton 的实例有问题,见谅哈大家,我回头也查查看是哪里的问题。