Electron 知识点
构建参数
compression
"store" | "normal" | "maximum"arch
"x64" | "ia32" | "armv7l" | "arm64" | "universal"requestedExecutionLevel
requestedExecutionLevel 有三个可选值:
- asInvoker:以启动该程序的用户权限运行(推荐)
- requireAdministrator:要求管理员权限(当前设置)
- highestAvailable:使用可用的最高权限
注意
将其改为 asInvoker 后:
- 应用将以普通用户权限运行
- 不会每次都弹出 UAC 提示
- 仍然可以在需要时请求提升权限
target
# The target package type: list of nsis, nsis-web (Web installer),
# portable ([portable]./nsis.md#portable) app without installation),
# appx, msi, msi-wrapped, squirrel, 7z, zip, tar.xz, tar.lz, tar.gz,
# tar.bz2, dir. AppX package can be built only on Windows 10.
default: nsisCJS、ESM
Electron 28 起,Electron 支持ECMAScript 模块(即使用 import 加载模块)。 您可以在我们的 ESM 指南 中找到有关 Electron 中 ESM 状态 以及如何在我们的应用程序中使用它们的更多信息。 Chromium 和 Node.js 有自己实现的 ESM 规格,Electron 根据上下文选择使用哪个模块加载器。 但是Electron中使用ESM的局限性, 以及Electron中的 ESM与Node.js和Chromium中的ESM之间的差异。
因此推荐选择CJS!
下表概述了什么是支持ESM的地方以及使用什么是ESM加载器:
| 流程 | ESM 加载器 | 预加载的 ESM 加载器 | 适用的要求 |
|---|---|---|---|
| 首页 | Node.js | N/A | - 你必须在 app 的 ready 事件之前执行顶级 await |
| 渲染进程(沙盒化) | Chromium | 不支持 | - 沙盒化预加载脚本不能使用 ESM 导入 |
| 渲染进程(非沙盒化 & 上下文隔离) | Chromium | Node.js | - 非沙盒化 ESM 预加载脚本将在没有内容的页面加载后运行- ESM 预加载脚本必须有 .mjs 扩展 |
| 渲染进程(非沙盒化 & 非上下文隔离) | Chromium | Node.js | - 非沙盒化 ESM 预加载脚本将在没有内容的页面加载后运行- ESM 预加载脚本必须有 .mjs 扩展名- ESM 预加载脚本必须是隔离上下文中才能使用动态 Node.js ESM 导入 |
注意
同时ESM得语法在:
- 主进程
- 渲染器进程
- Preload脚本
都有差异,因为在不同的进程中Chromium和Node.js的ESM加载器不同,所以在不同的进程中使用ESM的语法也不同, 大部分场景下需要.mjs扩展名。
综合所述,优先选择了CJS!
流程模型
多进程模型
为啥使用多进程模型
Chrome 团队决定让每个标签页在自己的进程中渲染, 从而限制了一个网页上的有误或恶意代码可能导致的对整个应用程序造成的伤害。 然后用单个浏览器进程控制这些标签页进程,以及整个应用程序的生命周期。
Electron 应用程序的结构非常相似。 作为应用开发者,你将控制两种类型的进程:主进程 和 渲染器进程。
主进程
每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。
窗口管理
BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。
const { BrowserWindow } = require('electron');
const win = new BrowserWindow({ width: 800, height: 1500 });
win.loadURL('https://github.com');
const contents = win.webContents;当一个 BrowserWindow 实例被销毁时,与其相应的渲染器进程也会被终止。
应用程序生命周期
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});原生 API
为了使 Electron 的功能不仅仅限于对网页内容的封装,主进程也添加了自定义的 API 来与用户的作业系统进行交互。 Electron 有着多种控制原生桌面功能的模块,例如菜单、对话框以及托盘图标。
渲染进程
每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。
- 您应用中的每个页面都在一个单独的进程中运行,我们称这些进程为 渲染器 (renderer)。
- Electron 目前只支持三个平台,是通过检查 Node.js 的 process.platform 变量确认的[ win32 (Windows), linux (Linux) 和 darwin (macOS) ]
Preload脚本
预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。、
应用示例
注意: 项目的中医保接口、机器信息等就是通过Preload脚本实现的,可进行参考。
其他
退出应用
// 适用于Windows和Linux平台。
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});打开窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});上下文隔离
上下文隔离是什么?
上下文隔离功能将确保您的 预加载脚本 和 Electron的内部逻辑 运行在所加载的 webcontent网页 之外的另一个独立的上下文环境里。 这对安全性很重要,因为它有助于阻止网站访问 Electron 的内部组件 和 您的预加载脚本可访问的高等级权限的API 。
这意味着,实际上,您的预加载脚本访问的 window 对象并不是网站所能访问的对象。 例如,如果您在预加载脚本中设置 window.hello = 'wave' 并且启用了上下文隔离,当网站尝试访问window.hello对象时将返回 undefined。
自 Electron 12 以来,默认情况下已启用上下文隔离,并且它是 所有应用程序推荐的安全设置。
安全事项
在预加载脚本中下列方式是不安全的:
// ❌ 错误使用
contextBridge.exposeInMainWorld('myAPI', {
send: ipcRenderer.send,
});正确的做法是需要使用参数过滤高等级权限:
// ✅ 正确使用
contextBridge.exposeInMainWorld('myAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs'),
});进程间通信
进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。 由于主进程和渲染器进程在 Electron 的进程模型具有不同的职责, 因此 IPC 是执行许多常见任务的唯一方法,例如从 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。
渲染器 => 主进程(单向)
参考错误页面的退出引用的实现,就是使用的渲染器 => 主进程单向通信。
渲染器 <=> 主进程(双向)
参考官网的打开文件上传。
主进程 => 渲染器
参考错误页面的重新加载的实现,就是使用的主进程 => 渲染器。
渲染器 => 渲染器
进程沙盒化
Electron中的沙盒
在 Electron 中沙盒进程 大部分地 表现都与 Chromium 差不多, 但因为介面是 Node.js 的关系 Electron 有一些额外的概念需要考虑。
渲染器进程
在沙盒中,渲染进程只能透过 进程间通讯 (inter-process communication, IPC) 委派任务给主进程的方式, 来执行需权限的任务。
配置沙盒
app.whenReady().then(() => {
const win = new BrowserWindow({
webPreferences: {
sandbox: false,
},
});
win.loadURL('https://google.com');
});
// 注意:在渲染器中启用 nodeIntegration 时,沙盒也会被禁用。
app.whenReady().then(() => {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
},
});
win.loadURL('https://google.com');
});全局启用沙盒
app.enableSandbox()
app.whenReady().then(() => }
// 因为调用了app.enableSandbox(),所以任何sandbox:false的调用都会被覆盖。
const win = new BrowserWindow()
win.loadURL('https://google.com')
})