跳到主要内容

Electron 内部原理:弱引用

·6 分钟阅读

作为一种具有垃圾回收功能的语言,JavaScript 将用户从手动管理资源中解放出来。但是,由于 Electron 托管此环境,因此它必须非常小心地避免内存和资源泄漏。

本文介绍了弱引用的概念以及它们如何在 Electron 中用于管理资源。


弱引用

在 JavaScript 中,每当您将对象分配给变量时,您就是在添加对该对象的引用。只要存在对该对象的引用,它就会始终保留在内存中。一旦对该对象的所有引用都消失了,即不再有变量存储该对象,JavaScript 引擎将在下次垃圾回收时回收内存。

弱引用是对对象的引用,它允许您获取该对象,而不会影响它是否会被垃圾回收。您还会在对象被垃圾回收时收到通知。然后,就可以使用 JavaScript 管理资源。

以 Electron 中的 NativeImage 类为例,每次您调用 nativeImage.create() API 时,都会返回一个 NativeImage 实例,它将图像数据存储在 C++ 中。一旦您完成该实例,并且 JavaScript 引擎 (V8) 已对该对象进行垃圾回收,则会调用 C++ 中的代码来释放内存中的图像数据,因此用户无需手动管理此操作。

另一个例子是窗口消失问题,该问题直观地展示了当对窗口的所有引用都消失时,窗口是如何被垃圾回收的。

在 Electron 中测试弱引用

由于该语言没有分配弱引用的方法,因此无法在原始 JavaScript 中直接测试弱引用。JavaScript 中唯一与弱引用相关的 API 是 WeakMap,但由于它只创建弱引用键,因此无法知道对象何时被垃圾回收。

在 v0.37.8 之前的 Electron 版本中,您可以使用内部 v8Util.setDestructor API 来测试弱引用,该 API 会向传递的对象添加弱引用,并在对象被垃圾回收时调用回调

// Code below can only run on Electron < v0.37.8.
var v8Util = process.atomBinding('v8_util');

var object = {};
v8Util.setDestructor(object, function () {
console.log('The object is garbage collected');
});

// Remove all references to the object.
object = undefined;
// Manually starts a GC.
gc();
// Console prints "The object is garbage collected".

请注意,您必须使用 --js-flags="--expose_gc" 命令开关启动 Electron 才能公开内部 gc 函数。

该 API 在后来的版本中被删除,因为 V8 实际上不允许在析构函数中运行 JavaScript 代码,并且在后来的版本中这样做会导致随机崩溃。

remote 模块中的弱引用

除了使用 C++ 管理本机资源外,Electron 还需要弱引用来管理 JavaScript 资源。一个示例是 Electron 的 remote 模块,它是一个 远程过程调用 (RPC) 模块,允许从渲染器进程中使用主进程中的对象。

remote 模块的一个关键挑战是避免内存泄漏。当用户在渲染器进程中获取远程对象时,remote 模块必须保证该对象在主进程中继续存在,直到渲染器进程中的引用消失。此外,它还必须确保当渲染器进程中不再有对该对象的任何引用时,可以对该对象进行垃圾回收。

例如,如果没有适当的实现,以下代码将很快导致内存泄漏

const { remote } = require('electron');

for (let i = 0; i < 10000; ++i) {
remote.nativeImage.createEmpty();
}

remote 模块中的资源管理很简单。每当请求对象时,都会向主进程发送一条消息,Electron 会将该对象存储在映射中并为其分配一个 ID,然后将 ID 发送回渲染器进程。在渲染器进程中,remote 模块将接收 ID 并将其包装在一个代理对象中,当代理对象被垃圾回收时,将向主进程发送一条消息以释放该对象。

remote.require API 为例,简化的实现如下所示

remote.require = function (name) {
// Tell the main process to return the metadata of the module.
const meta = ipcRenderer.sendSync('REQUIRE', name);
// Create a proxy object.
const object = metaToValue(meta);
// Tell the main process to free the object when the proxy object is garbage
// collected.
v8Util.setDestructor(object, function () {
ipcRenderer.send('FREE', meta.id);
});
return object;
};

在主进程中

const map = {};
const id = 0;

ipcMain.on('REQUIRE', function (event, name) {
const object = require(name);
// Add a reference to the object.
map[++id] = object;
// Convert the object to metadata.
event.returnValue = valueToMeta(id, object);
});

ipcMain.on('FREE', function (event, id) {
delete map[id];
});

带有弱值的映射

使用之前的简单实现,remote 模块中的每次调用都将从主进程返回一个新的远程对象,并且每个远程对象都表示对主进程中对象的引用。

该设计本身没有问题,但问题在于当多次调用以接收同一对象时,会创建多个代理对象,对于复杂的对象,这会给内存使用和垃圾回收带来巨大的压力。

例如,以下代码

const { remote } = require('electron');

for (let i = 0; i < 10000; ++i) {
remote.getCurrentWindow();
}

它首先使用大量内存创建代理对象,然后占用 CPU(中央处理单元)来对它们进行垃圾回收并发送 IPC 消息。

一个明显的优化是缓存远程对象:当已经存在具有相同 ID 的远程对象时,将返回先前的远程对象,而不是创建一个新的远程对象。

这对于 JavaScript 核心中的 API 是不可能的。使用普通映射来缓存对象将阻止 V8 对对象进行垃圾回收,而 WeakMap 类只能使用对象作为弱键。

为了解决这个问题,添加了一种以弱引用作为值的映射类型,这非常适合使用 ID 缓存对象。现在 remote.require 看起来像这样

const remoteObjectCache = v8Util.createIDWeakMap()

remote.require = function (name) {
// Tell the main process to return the meta data of the module.
...
if (remoteObjectCache.has(meta.id))
return remoteObjectCache.get(meta.id)
// Create a proxy object.
...
remoteObjectCache.set(meta.id, object)
return object
}

请注意,remoteObjectCache 将对象存储为弱引用,因此当对象被垃圾回收时,无需删除键。

本机代码

对于那些对 Electron 中弱引用的 C++ 代码感兴趣的人,可以在以下文件中找到它

setDestructor API

createIDWeakMap API

2016 年 8 月:新应用

·3 分钟阅读

以下是八月份添加到网站上的新 Electron 应用程序。


该网站通过社区的 拉取请求更新新的应用程序聚会。 您可以关注该存储库以获取新添加内容的通知,或者如果您对网站的所有更改不感兴趣,请订阅博客 RSS 源

如果您制作了 Electron 应用程序或主办了聚会,请提交拉取请求将其添加到网站,它将出现在下一轮汇总中。

新应用

Code RPGifyRPG 风格的编码应用程序
PamFax用于发送和接收传真的跨平台应用程序
BlankUp清晰度 +1 的 Markdown 编辑器
Rambox免费开源的消息传递和电子邮件应用程序,将常用的 Web 应用程序组合到一个中
Gordie您卡片收藏的最佳应用程序
Ionic Creator更快地构建出色的移动应用程序
TwitchAlerts用漂亮的警报和通知让您的观众满意
Museeks简单、干净且跨平台的音乐播放器
SeaPig从 Markdown 到 HTML 的转换器
GroupMe非官方的 GroupMe 应用程序
Moeditor您通用的 Markdown 编辑器
SoundnodeSoundnode 应用程序是桌面的 Soundcloud
QMUI WebQMUI Web Desktop 是一个用于管理基于 QMUI Web 框架的项目的应用程序
Svgsus组织、清理和转换您的 SVG
Ramme非官方的 Instagram 桌面应用程序
InsomniaREST API 客户端
Correo适用于 Windows、macOS 和 Linux 的菜单栏/任务栏 Gmail 应用程序
KongDashKong 管理 API 的桌面客户端
翻译编辑器用于 INTL ICU 消息的翻译文件编辑器(请参阅 formatjsio)
5EClient5EPlay CSGO 客户端
Theme Juice轻松进行本地 WordPress 开发

辅助工具

·2 分钟阅读

创建可访问的应用程序非常重要,我们很高兴向DevtronSpectron引入新功能,让开发人员有机会让他们的应用程序对所有人更好。


Electron 应用程序中的可访问性问题与网站的相似,因为它们最终都是 HTML。 但是,对于 Electron 应用程序,您不能使用在线资源进行可访问性审核,因为您的应用程序没有 URL 可以指向审核员。

这些新功能将这些审核工具带到您的 Electron 应用程序中。 您可以选择使用 Spectron 将审核添加到您的测试中,或使用 Devtron 在 DevTools 中使用它们。 请继续阅读以了解这些工具的摘要,或查看我们的可访问性文档以获取更多信息。

Spectron

在测试框架 Spectron 中,您现在可以审核应用程序中的每个窗口和 <webview> 标签。 例如

app.client.auditAccessibility().then(function (audit) {
if (audit.failed) {
console.error(audit.message);
}
});

您可以在 Spectron 的文档中阅读有关此功能的更多信息。

Devtron

在 Devtron 中,有一个新的辅助功能选项卡,可让您审核应用程序中的页面、排序和过滤结果。

devtron screenshot

这两个工具都使用 Google 为 Chrome 构建的 辅助功能开发人员工具库。 您可以在该存储库的 wiki上了解有关此库使用的可访问性审核规则的更多信息。

如果您知道其他出色的 Electron 辅助功能工具,请通过拉取请求将其添加到辅助功能文档中。

npm install electron

·3 分钟阅读

从 Electron 1.3.1 版本开始,您可以 npm install electron --save-dev 以在您的应用程序中安装最新的预编译 Electron 版本。


npm install electron

预构建的 Electron 二进制文件

如果您以前使用过 Electron 应用程序,您可能遇到过 electron-prebuilt npm 包。 该软件包几乎是每个 Electron 项目不可或缺的一部分。 安装后,它会检测您的操作系统并下载一个预构建的二进制文件,该二进制文件已编译为可在您系统的架构上运行。

新名称

Electron 安装过程通常是新开发人员的绊脚石。 许多勇敢的人试图通过运行 npm install electron 而不是 npm install electron-prebuilt 来开始开发 Electron 应用程序,结果发现(通常在很多困惑之后)它不是他们正在寻找的 electron

这是因为在 GitHub 的 Electron 项目存在之前,npm 上已经存在一个现有的 electron 项目。 为了使 Electron 开发对新开发人员来说更轻松、更直观,我们联系了现有 electron npm 包的所有者,询问他是否愿意让我们使用该名称。 幸运的是,他是我们项目的粉丝,并同意帮助我们重新利用该名称。

预构建仍然存在

从 1.3.1 版本开始,我们已经开始将 electronelectron-prebuilt 软件包同时发布到 npm。 这两个软件包是相同的。 我们选择在一段时间内继续以两个名称发布该软件包,以免给目前在其项目中使用 electron-prebuilt 的数千名开发人员带来不便。 我们建议更新您的 package.json 文件以使用新的 electron 依赖项,但我们将继续发布 electron-prebuilt 的新版本,直到 2016 年底。

electron-userland/electron-prebuilt 存储库仍然是 electron npm 包的规范主页。

非常感谢

我们要特别感谢 @mafintosh@maxogden 和许多其他 贡献者,感谢他们创建和维护 electron-prebuilt,以及他们为 JavaScript、Node.js 和 Electron 社区做出的不懈服务。

还要感谢 @logicalparadox 允许我们接管 npm 上的 electron 软件包。

更新您的项目

我们已经与社区合作,更新了受此更改影响的常用软件包。 electron-packagerelectron-rebuildelectron-builder 等软件包已经更新为可使用新名称,同时继续支持旧名称。

如果您在安装此新软件包时遇到任何问题,请在 electron-userland/electron-prebuilt 存储库上打开一个问题让我们知道。

对于 Electron 的任何其他问题,请使用 electron/electron 存储库。

Electron 内部原理:将 Node 用作库

·4 分钟阅读

这是解释 Electron 内部结构系列文章的第二篇。 如果您还没有看过,请查看关于事件循环集成的第一篇文章

大多数人使用Node 进行服务器端应用程序,但由于 Node 丰富的 API 集和蓬勃发展的社区,它也非常适合嵌入式库。 这篇文章解释了 Node 在 Electron 中如何用作库。


构建系统

Node 和 Electron 都使用GYP 作为其构建系统。 如果您想将 Node 嵌入到您的应用程序中,您也必须将其用作您的构建系统。

不熟悉 GYP? 在您继续阅读本文之前,请阅读本指南

Node 的标志

Node 源代码目录中的 node.gyp 文件描述了 Node 的构建方式,以及许多GYP 变量,这些变量控制启用 Node 的哪些部分以及是否打开某些配置。

要更改构建标志,您需要在项目的 .gypi 文件中设置变量。 Node 中的 configure 脚本可以为您生成一些通用配置,例如运行 ./configure --shared 将生成一个 config.gypi,其中包含指示 Node 构建为共享库的变量。

Electron 不使用 configure 脚本,因为它有自己的构建脚本。Node 的配置在 Electron 根源代码目录下的 common.gypi 文件中定义。

在 Electron 中,Node 通过将 GYP 变量 node_shared 设置为 true 而作为共享库链接,因此 Node 的构建类型将从 executable 更改为 shared_library,并且包含 Node 的 main 入口点的源代码将不会被编译。

由于 Electron 使用 Chromium 自带的 V8 库,因此不会使用 Node 源代码中包含的 V8 库。这是通过将 node_use_v8_platformnode_use_bundled_v8 都设置为 false 来实现的。

共享库或静态库

在与 Node 链接时,有两种选择:您可以将 Node 构建为静态库并将其包含在最终可执行文件中,或者您可以将其构建为共享库并与最终可执行文件一起发布。

在 Electron 中,Node 长期以来都是作为静态库构建的。这使得构建过程简单,可以实现最佳的编译器优化,并允许 Electron 在没有额外的 node.dll 文件的情况下分发。

然而,在 Chrome 切换到使用 BoringSSL 后,情况发生了变化。BoringSSL 是 OpenSSL 的一个分支,它删除了几个未使用的 API 并更改了许多现有的接口。由于 Node 仍然使用 OpenSSL,如果它们链接在一起,编译器会由于符号冲突而生成大量链接错误。

Electron 无法在 Node 中使用 BoringSSL,也无法在 Chromium 中使用 OpenSSL,因此唯一的选择是将 Node 构建为共享库,并在每个组件中隐藏 BoringSSL 和 OpenSSL 符号

这个更改给 Electron 带来了一些积极的副作用。在此更改之前,如果您使用了本机模块,则无法在 Windows 上重命名 Electron 的可执行文件,因为可执行文件的名称被硬编码在导入库中。在将 Node 构建为共享库后,这个限制消失了,因为所有本机模块都链接到了 node.dll,而 node.dll 的名称不需要更改。

支持本机模块

Node 中的本机模块的工作原理是定义一个 Node 加载的入口函数,然后从 Node 中搜索 V8 和 libuv 的符号。对于嵌入器来说,这有点麻烦,因为默认情况下,当将 Node 构建为库时,V8 和 libuv 的符号会被隐藏,本机模块将无法加载,因为它们找不到这些符号。

因此,为了使本机模块正常工作,V8 和 libuv 的符号在 Electron 中被公开。对于 V8,这是通过强制公开 Chromium 配置文件中的所有符号来实现的。对于 libuv,这是通过设置 BUILDING_UV_SHARED=1 定义来实现的。

在你的应用程序中启动 Node

在完成所有构建和与 Node 链接的工作后,最后一步是在你的应用程序中运行 Node。

Node 没有提供很多公共 API 来将其自身嵌入到其他应用程序中。通常,你可以直接调用 node::Startnode::Init 来启动一个新的 Node 实例。但是,如果你正在构建一个基于 Node 的复杂应用程序,你必须使用像 node::CreateEnvironment 这样的 API 来精确控制每个步骤。

在 Electron 中,Node 以两种模式启动:在主进程中运行的独立模式,类似于官方的 Node 二进制文件,以及将 Node API 插入到网页中的嵌入模式。具体细节将在以后的文章中解释。

2016 年 7 月:新应用和聚会

·2 分钟阅读

我们正在开始每月一次的汇总,以突出 Electron 社区的活动。每次汇总将包含新应用程序、即将举行的聚会、工具、视频等内容。


此站点通过社区的 拉取请求更新了新的应用程序聚会。您可以关注该存储库以获取新添加内容的通知,或者如果您对该站点的 *所有* 更改不感兴趣,请订阅博客 RSS 订阅源

如果您制作了 Electron 应用程序或主办了聚会,请提交拉取请求将其添加到网站,它将出现在下一轮汇总中。

新应用

Demio一个为入站销售和营销构建的在线研讨会平台
ElectorrentuTorrent 服务器的远程客户端应用程序
PhoneGap一个开源框架,使您可以使用 Web 技术构建出色的移动应用程序
WordMark一个为 Markdown 作者提供的轻量级博客发布编辑器
UbAuth一个帮助开发人员使用 OAuth 2.0 为 Uber 应用程序创建访问令牌的应用程序
HyperTermHTML/JS/CSS 终端
MarpMarkdown 演示文稿编写器
Glyphr Studio一个免费的、基于 Web 的字体设计器,专注于为爱好者进行字体设计
BitCrypt一个简单的 Windows 文件加密应用程序,用于加密您的数据
Trym一个美观的 macOS 小应用程序,帮助您查看、优化和转换 SVG 图标
Booker一个具有 Markdown 功能的文本编辑器
PhonePresenter最智能的演示文稿点击器
Yout一种在桌面上观看 YouTube 播放列表的新方式

新的聚会

Electron 开源桌面框架英国伦敦

Electron 内部原理:消息循环集成

·3 分钟阅读

这是解释 Electron 内部原理的系列文章的第一篇。这篇文章介绍了 Node 的事件循环如何在 Electron 中与 Chromium 集成。


已经有很多尝试使用 Node 进行 GUI 编程,例如 node-gui 用于 GTK+ 绑定,以及 node-qt 用于 QT 绑定。但它们都无法在生产中使用,因为 GUI 工具包有自己的消息循环,而 Node 使用 libuv 用于自己的事件循环,主线程一次只能运行一个循环。因此,在 Node 中运行 GUI 消息循环的常见技巧是以非常小的时间间隔在计时器中泵消息循环,这使得 GUI 界面响应缓慢并占用大量 CPU 资源。

在 Electron 的开发过程中,我们也遇到了同样的问题,尽管方式相反:我们必须将 Node 的事件循环集成到 Chromium 的消息循环中。

主进程和渲染进程

在我们深入研究消息循环集成的细节之前,我将首先解释 Chromium 的多进程架构。

在 Electron 中,有两种类型的进程:主进程和渲染进程(实际上这非常简化,有关完整视图,请参阅多进程架构)。主进程负责 GUI 工作,如创建窗口,而渲染进程只负责运行和渲染网页。

Electron 允许使用 JavaScript 来控制主进程和渲染进程,这意味着我们必须将 Node 集成到这两个进程中。

用 libuv 替换 Chromium 的消息循环

我的第一次尝试是用 libuv 重新实现 Chromium 的消息循环。

对于渲染进程来说很容易,因为它的消息循环只监听文件描述符和计时器,我只需要用 libuv 实现接口。

然而,对于主进程来说,这要困难得多。每个平台都有自己的 GUI 消息循环。macOS Chromium 使用 NSRunLoop,而 Linux 使用 glib。我尝试了很多技巧来从本机 GUI 消息循环中提取底层文件描述符,然后将它们提供给 libuv 进行迭代,但我仍然遇到了不起作用的边缘情况。

因此,我最终添加了一个计时器,以小的时间间隔轮询 GUI 消息循环。结果,该进程占用了恒定的 CPU 使用率,并且某些操作有很长的延迟。

在单独的线程中轮询 Node 的事件循环

随着 libuv 的成熟,可以采取另一种方法。

后端 fd 的概念被引入到 libuv 中,这是一个 libuv 为其事件循环轮询的文件描述符(或句柄)。因此,通过轮询后端 fd,可以在 libuv 中有新事件时获得通知。

所以在 Electron 中,我创建了一个单独的线程来轮询后端 fd,并且由于我使用的是系统调用进行轮询而不是 libuv API,因此它是线程安全的。并且每当 libuv 的事件循环中有新事件时,都会向 Chromium 的消息循环发布一条消息,然后 libuv 的事件将在主线程中处理。

这样,我避免了修补 Chromium 和 Node,并且主进程和渲染进程都使用了相同的代码。

代码

您可以在 electron/atom/common/ 下的 node_bindings 文件中找到消息循环集成的实现。它可以很容易地用于想要集成 Node 的项目。

更新:实现已移至 electron/shell/common/node_bindings.cc

Electron 播客

·一分钟阅读

正在寻找 Electron 的介绍吗?刚刚发布了两个新的播客,它们概述了 Electron 是什么、为什么构建它以及如何使用它。


现在发布

Hanselminutes:使用 Jessica Lord 创建跨平台 Electron 应用程序

Electron 是“仅仅是框架中的 Chrome”还是远不止于此?Jessica 为 Scott 指明了正确的方向,并准确地解释了 Electron 平台在您的开发世界中的位置。


JavaScript Air:Electron 应用程序

Electron 正在成为一种越来越相关和流行的使用 Web 技术构建多平台桌面应用程序的方式。让我们深入了解这项令人敬畏的技术,看看如何使用它来增强我们自己的体验和用户在桌面上的体验。


如果您正在寻找 Electron 的入门介绍,请先听第一个播客。第二个播客将更详细地介绍如何构建应用程序,其中包含来自 Nylas 的 Evan Morikawa 的精彩提示。

我们目前正在制作另外两个播客,预计将于下个月发布,请关注 @ElectronJS Twitter 账号以获取更新。

Electron 1.0

·4 分钟阅读

在过去的两年里,Electron 帮助开发者使用 HTML、CSS 和 JavaScript 构建跨平台的桌面应用程序。现在,我们很高兴分享我们的框架和创建它的社区的一个重要里程碑。Electron 1.0 版本现已在 electronjs.org 上发布。


Electron 1.0

Electron 1.0 代表了 API 稳定性和成熟度方面的一个重要里程碑。此版本允许您构建在 Windows、Mac 和 Linux 上表现和感觉真正原生的应用程序。借助新的文档、新工具和一个新的应用程序来引导您了解 Electron API,构建 Electron 应用程序比以往任何时候都更加容易。

如果您准备构建您的第一个 Electron 应用程序,这里有一个快速入门指南来帮助您入门。

我们很高兴看到您接下来使用 Electron 构建什么。

Electron 的发展历程

我们在推出 Atom 时发布了 Electron,那是两年多前的事了。当时被称为 Atom Shell 的 Electron 是我们构建 Atom 的框架。在那段日子里,Atom 是 Electron 所提供功能和特性的驱动力,我们努力发布 Atom 的初始版本。

现在驱动 Electron 的是一个不断增长的开发者和公司社区,他们构建了从电子邮件聊天Git 应用程序SQL 分析工具BT 客户端机器人的各种应用。

在过去的两年里,我们看到公司和开源项目都选择 Electron 作为其应用程序的基础。仅在去年,Electron 的下载量就超过 120 万次。 浏览一些令人惊叹的 Electron 应用程序,如果您的应用程序不在其中,请添加它。

Electron downloads

Electron API 演示

随着 1.0 版本的发布,我们还发布了一个新应用程序,以帮助您探索 Electron API,并了解更多关于如何使您的 Electron 应用程序感觉像原生应用程序的信息。Electron API 演示应用程序包含代码片段,可帮助您启动应用程序并提供有关有效使用 Electron API 的提示。

Electron API Demos

Devtron

我们还添加了一个新的扩展来帮助您调试 Electron 应用程序。DevtronChrome 开发者工具的一个开源扩展,旨在帮助您检查、调试和排除 Electron 应用程序的故障。

Devtron

功能

  • Require 图表,可帮助您可视化应用程序在主进程和渲染器进程中的内部和外部库依赖关系
  • IPC 监视器,用于跟踪和显示应用程序中进程之间发送和接收的消息
  • 事件检查器,向您显示在窗口、应用程序和进程等核心 Electron API 上注册的事件和监听器
  • App Linter,用于检查您的应用程序是否存在常见错误和缺少的功能

Spectron

最后,我们正在发布新版本的 Spectron,这是 Electron 应用程序的集成测试框架。

Spectron

Spectron 3.0 对整个 Electron API 提供了全面的支持,使您能够更快地编写测试,以验证您的应用程序在各种场景和环境中的行为。Spectron 基于 ChromeDriverWebDriverIO,因此它还具有用于页面导航、用户输入和 JavaScript 执行的完整 API。

社区

Electron 1.0 是数百名开发者共同努力的结果。在核心框架之外,已经发布了数百个库和工具,以使构建、打包和部署 Electron 应用程序更加容易。

现在有一个新的社区页面,列出了许多正在开发的很棒的 Electron 工具、应用程序、库和框架。您还可以查看 ElectronElectron Userland 组织,以查看其中一些出色的项目。

Electron 新手?观看 Electron 1.0 介绍视频

Electron 0.37 中的新功能

·4 分钟阅读

Electron 0.37 最近已发布,其中包括从 Chrome 47 到 Chrome 49 的重大升级,以及几个新的核心 API。此最新版本带来了 Chrome 48Chrome 49 中发布的所有新功能。这包括 CSS 自定义属性、增加的 ES6 支持、KeyboardEvent 改进、Promise 改进以及许多其他现在可以在您的 Electron 应用程序中使用的新功能。


新功能

CSS 自定义属性

如果您使用过 Sass 和 Less 等预处理语言,您可能熟悉变量,它允许您为配色方案和布局等内容定义可重用的值。变量有助于保持样式表的 DRY 并更易于维护。

CSS 自定义属性类似于预处理变量,因为它们是可重用的,但它们也具有一个独特的特性,使其更加强大和灵活:可以使用 JavaScript 操作它们。这个细微但强大的功能允许动态更改视觉界面,同时仍然受益于CSS 的硬件加速,并减少前端代码和样式表之间的代码重复。

有关 CSS 自定义属性的更多信息,请参阅 MDN 文章Google Chrome 演示

CSS 变量在实际应用中

让我们浏览一个简单的变量示例,该示例可以在您的应用程序中实时调整。

:root {
--awesome-color: #a5ecfa;
}

body {
background-color: var(--awesome-color);
}

可以在 JavaScript 中直接检索和更改变量值

// Get the variable value ' #A5ECFA'
let color = window
.getComputedStyle(document.body)
.getPropertyValue('--awesome-color');

// Set the variable value to 'orange'
document.body.style.setProperty('--awesome-color', 'orange');

也可以从开发工具的样式部分编辑变量值,以获得快速反馈和调整

CSS properties in Styles tab

KeyboardEvent.code 属性

Chrome 48 添加了新的 code 属性,该属性在 KeyboardEvent 事件中可用,它将是按下的物理键,独立于操作系统键盘布局。

这应该使在您的 Electron 应用程序中实现自定义键盘快捷方式在不同的机器和配置中更加准确和一致。

window.addEventListener('keydown', function (event) {
console.log(`${event.code} was pressed.`);
});

查看此示例以查看其运行情况。

Promise 拒绝事件

Chrome 49 添加了两个新的 window 事件,允许您在被拒绝的 Promise 未被处理时收到通知。

window.addEventListener('unhandledrejection', function (event) {
console.log('A rejected promise was unhandled', event.promise, event.reason);
});

window.addEventListener('rejectionhandled', function (event) {
console.log('A rejected promise was handled', event.promise, event.reason);
});

查看 此示例以查看其运行情况。

V8 中的 ES2015 更新

现在 Electron 中的 V8 版本包含 91% 的 ES2015。以下是一些您可以开箱即用的有趣补充 - 无需标志或预编译器

默认参数

function multiply(x, y = 1) {
return x * y;
}

multiply(5); // 5

解构赋值

Chrome 49 添加了解构赋值,使赋值变量和函数参数更容易。

这使得 Electron 的要求更简洁,并且现在可以更紧凑地进行分配

浏览器进程的要求
const { app, BrowserWindow, Menu } = require('electron');
渲染器进程的要求
const { dialog, Tray } = require('electron').remote;
其他示例
// Destructuring an array and skipping the second element
const [first, , last] = findAll();

// Destructuring function parameters
function whois({ displayName: displayName, fullName: { firstName: name } }) {
console.log(`${displayName} is ${name}`);
}

let user = {
displayName: 'jdoe',
fullName: {
firstName: 'John',
lastName: 'Doe',
},
};
whois(user); // "jdoe is John"

// Destructuring an object
let { name, avatar } = getUser();

新的 Electron API

以下是一些新的 Electron API,您可以在 Electron 版本的发行说明中查看每个新的 API。

BrowserWindow 上的 showhide 事件

这些事件在窗口显示或隐藏时发出。

const { BrowserWindow } = require('electron');

let window = new BrowserWindow({ width: 500, height: 500 });
window.on('show', function () {
console.log('Window was shown');
});
window.on('hide', function () {
console.log('Window was hidden');
});

OS Xappplatform-theme-changed 事件

当系统深色模式主题切换时,会发出此事件。

const { app } = require('electron');

app.on('platform-theme-changed', function () {
console.log(`Platform theme changed. In dark mode? ${app.isDarkMode()}`);
});

OS Xapp.isDarkMode() 方法

如果系统处于深色模式,此方法返回 true,否则返回 false

OS XBrowserWindowscroll-touch-beginscroll-touch-end 事件

当滚轮事件阶段开始或结束时,会发出这些事件。

const { BrowserWindow } = require('electron');

let window = new BrowserWindow({ width: 500, height: 500 });
window.on('scroll-touch-begin', function () {
console.log('Scroll touch started');
});
window.on('scroll-touch-end', function () {
console.log('Scroll touch ended');
});