跳到主要内容

Electron 基础

官方文档已经写的比较全了,所以并不会每个知识点都出现。

Electron 的主从进程模型是基本的常识。每个 Electron 应用有且只要一个主进程(Main Process)、以及一个或多个渲染进程(Renderer Process), 对应多个 Web 页面。更多更完整的请查看官方文档

项目最小组成:

1659-MGw7lM

多进程模型

Electron 继承了来自 Chromium 的多进程架构,这使得此框架在架构上非常相似于一个现代的网页浏览器。electron 框架的机制是把主进程和渲染进程分开的,所以这就导致了 electron 项目通常也是把主进程和渲染进程分开处理。所以我们在查阅一些相关的开源项目的时候,为了方便项目管理通常会发现:

  • 大部分项目处理自身 electron 主进程相关的内容会放在一个独立的文件夹里,比如,main。
  • 而另外渲染页面及处理页面逻辑的内容也是放在一个独立的文件夹,比如,render。

查看 electron 流程模型

1724-wiT1vJ

1520-7DRsOS

可以发现,主线程和渲染线程都集成了 Native API 和 Node.js,渲染线程还集成 Chromium 内核,成功实现跨端开发。

PS:调试信息,主进程的只会在编辑器里控制台显示。

主进程

任何 Electron 应用程序的入口都是 main 文件。这个文件控制了主进程,它运行在一个完整的 Node.js 环境中,负责控制您应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程。主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。一个 Electron 应用有且只有⼀个主进程。

执行期间,Electron 将依据应用中 package.json 配置下 main 字段中配置的值查找此文件。即 main 字段对应的文件即为主进程的入口文件,基于主进程后再执行渲染进程。

主进程的主要目的是:

  • 创建渲染进程,使用 BrowserWindow 模块创建和管理应用程序窗口。一个 BrowserWindow 里又可以承载多个 BrowserView。
  • 管理原生 GUI,典型的窗口(BrowserWindow、Tray、Dock、Menu)等
  • 控制应用生命周期

窗口管理

BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。您可从主进程用 window 的 webContent 对象与网页内容进行交互。

由于 BrowserWindow 模块是一个 EventEmitter,所以您也可以为各种用户事件(例如,最小化或最大化您的窗口)添加处理程序。

当一个 BrowserWindow 实例被销毁时,与其相应的渲染器进程也会被终止。

应用程序生命周期

主进程还能通过 Electron 的 app 模块来控制您应用程序的生命周期。该模块提供了一整套的事件和方法,可以使你添加自定义的应用程序行为(例如:以编程方式退出您的应用程序、修改程序坞或显示关于面板)。

渲染进程

每个 Electron 应用都会为每个打开的 BrowserWindow(与每个网页嵌入)生成一个单独的渲染进程。洽如其名,渲染器负责渲染网页内容。所以实际上,运行于渲染器进程中的代码是须遵照网页标准的(至少就目前使用的 Chromium 而言是如此)。

因此,一个浏览器窗口中的所有的用户界面和应用功能,都应与您在网页开发上使用相同的工具和规范来进行编写。

渲染器无权直接访问 require 或其他 Node.js API。

此刻,您或许会好奇:既然这些特性只能由主进程访问,那渲染器进程用户界面怎样才能与 Node.js 和 Electron 的原生桌面功能进行交互。而事实上,确实没有直接导入 Electron 內容脚本的方法。

PS:BrowserView 被用来让 BrowserWindow 嵌入更多的 web 内容。它就像一个子窗口,除了它的位置是相对于父窗口。

Preload 脚本

预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码。这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。

预加载脚本可以在 BrowserWindow 构造方法中的 webPreferences 选项里被附加到主进程。

// main.js
const { BrowserWindow } = require("electron");
//...
const win = new BrowserWindow({
webPreferences: {
preload: "path/to/preload.js",
},
});
//...

因为预加载脚本与浏览器共享同一个全局 Window 接口,并且可以访问 Node.js API,所以它通过在全局 window 中暴露任意 API 来增强渲染器,以便你的网页内容使用。

虽然预加载脚本与其所附着的渲染器在共享着一个全局 window 对象,但您并不能从中直接附加任何变动到 window 之上,因为 contextIsolation 上下文隔离 是默认的。

语境隔离(Context Isolation)意味着预加载脚本与渲染器的主要运行环境是隔离开来的,以避免泄漏任何具特权的 API 到您的网页内容代码中。

取而代之,我们將使用 contextBridge 模块来安全地实现交互:

// preload.js
const { contextBridge } = require("electron");

contextBridge.exposeInMainWorld("myAPI", {
desktop: true,
});
// renderer.js
console.log(window.myAPI);
// => { desktop: true }

此功能对两个主要目的来说非常有用:

  • 通过暴露 ipcRenderer 帮手模块于渲染器中,您可以使用 进程间通讯(inter-process communication, IPC) 来从渲染器触发主进程任务(反之亦然)。
  • 如果您正在为远程 URL 上托管的现有 web 应用开发 Electron 封裝,则您可在渲染器的 window 全局变量上添加自定义的属性,好在 web 客户端用上仅适用于桌面应用的设计逻辑 。

进程间通信

非常重要

Webview

sandbox 进程沙盒化

在 Electron 中沙盒进程 大部分地 表现都与 Chromium 差不多, 但因为介面是 Node.js 的关系 Electron 有一些额外的概念需要考虑。

渲染器进程

当 Electron 中的渲染进程被沙盒化时,它们的行为与常规 Chrome 渲染器一样。 一个沙盒化的渲染器不会有一个 Node.js 环境。

因此,在沙盒中,渲染进程只能透过 进程间通讯 (inter-process communication, IPC) 委派任务给主进程的方式, 来执行需权限的任务 (例如:文件系统交互,对系统进行更改或生成子进程) 。

Preload 脚本

为了让渲染进程能与主进程通信,附属于沙盒化的渲染进程的 preload 脚本中仍可使用一部分以 Polyfill 形式实现的 Node.js API。 有一个与 Node 中类似的 require 函数提供了出来,但只能载入 Electron 和 Node 内置模块的一个子集:

  • electron (仅限渲染器进程模块)
  • 事件
  • timers
  • url

此外,以下 Node.js 基础对象也填充到了 preload 脚本的全局上下文中:

  • Buffer
  • process
  • clearImmediate
  • setImmediate

Node C++ Addons(扩展)介绍

C++ 编写的动态链接共享对象,能被 Node.js require 使用

.node 文件 本质是动态链接库(Windows 的 *.dll,Mac 的 *.dylib,Linux 的*.so)

编写 C++ 扩展主流 2 种写法:

  • NAN (Native Abstractions for Node.js):一次编写、到处编译
  • N-API (Node.js 一部分,独立于 runtime v8):同 ABI(Application Binary Interface,应用二进制接口)、无需重新编译
    • 本身是基于 C 的 API
    • C++ 封装 node-addon-api

扩展资料:

其他

从 V12 版本后,remote 模块已被移出 electron,需要自行安装此模块。

// in the renderer process:

// Before
const { BrowserWindow } = require("electron").remote;

// After
const { BrowserWindow } = require("@electron/remote");