Skip to main content

证书透明度修复

· 5 min read

Electron 1.4.12 包含一个重要补丁,该补丁修复了 Chrome 上游的一个问题,即在 Electron 的底层 Chrome 库 libchromiumcontent 构建 10 周后,部分 Symantec、GeoTrust 和 Thawte SSL/TLS 证书被错误拒绝。受影响网站使用的证书没有问题,更换这些证书也无济于事。

¥Electron 1.4.12 contains an important patch that fixes an upstream Chrome issue where some Symantec, GeoTrust, and Thawte SSL/TLS certificates are incorrectly rejected 10 weeks from the build time of libchromiumcontent, Electron's underlying Chrome library. There are no issues with the certificates used on the affected sites and replacing these certificates will not help.


在 Electron 1.4.0 - 1.4.11 中,使用这些受影响证书的站点的 HTTPS 请求将在特定日期后因网络错误而失败。这会影响使用 Chrome 底层网络 API 发出的 HTTPS 请求,例如 window.fetch、Ajax 请求、Electron 的 net API、BrowserWindow.loadURLwebContents.loadURL<webview> 标签上的 src 属性等等。

¥In Electron 1.4.0 — 1.4.11 HTTPS requests to sites using these affected certificates will fail with network errors after a certain date. This affects HTTPS requests made using Chrome's underlying networking APIs such as window.fetch, Ajax requests, Electron's net API, BrowserWindow.loadURL, webContents.loadURL, the src attribute on a <webview> tag, and others.

将应用升级到 1.4.12 将避免出现这些请求失败的情况。

¥Upgrading your applications to 1.4.12 will prevent these request failures from occurring.

注意:此问题在 Chrome 53 中引入,因此 Electron 1.4.0 之前的版本不受影响。

¥Note: This issue was introduced in Chrome 53 so Electron versions earlier than 1.4.0 are not affected.

影响日期

¥Impact Dates

下表列出了 Electron 1.4 的各个版本,以及使用这些受影响证书的站点的请求开始失败的日期。

¥Below is a table of each Electron 1.4 version and the date when requests to sites using these affected certificates will start to fail.

Electron 版本影响日期
1.3.x不受影响
1.4.0已失败
1.4.1已失败
1.4.2已失败
1.4.32016 年 12 月 10 日 太平洋标准时间晚上 9:00
1.4.42016 年 12 月 10 日 太平洋标准时间晚上 9:00
1.4.52016 年 12 月 10 日 太平洋标准时间晚上 9:00
1.4.62017 年 1 月 14 日 太平洋标准时间晚上 9:00
1.4.72017 年 1 月 14 日 太平洋标准时间晚上 9:00
1.4.82017 年 1 月 14 日 太平洋标准时间晚上 9:00
1.4.92017 年 1 月 14 日 太平洋标准时间晚上 9:00
1.4.102017 年 1 月 14 日 太平洋标准时间晚上 9:00
1.4.112017 年 2 月 11 日 太平洋标准时间晚上 9:00
1.4.12不受影响

你可以通过将计算机时钟调快,然后检查 https://symbeta.symantec.com/welcome/ 是否成功加载来验证应用的生效日期。

¥You can verify your app's impact date by setting your computer's clock ahead and then check to see if https://symbeta.symantec.com/welcome/ successfully loads from it.

更多信息

¥More Information

你可以在以下位置阅读有关此主题、原始问题和修复的更多信息:

¥You can read more about this topic, the original issue, and the fix at the following places:

2016 年 9 月:新应用

· 5 min read

以下是 9 月份添加到网站的全新 Electron 应用和讲座。

¥Here are the new Electron apps and talks that were added to the site in September.


本网站已更新来自社区的新 appsmeetups拉取请求。你可以订阅 查看代码库 以获取新增内容的通知,或者如果你对网站的所有更改不感兴趣,请订阅 博客 RSS 订阅

¥This site is updated with new apps and meetups through pull requests from the community. You can watch the repository to get notifications of new additions or if you're not interested in all of the site's changes, subscribe to the blog RSS feed.

如果你开发过 Electron 应用或举办过聚会,请创建一个 拉取请求 并将其添加到网站,它将出现在下一轮总结中。

¥If you've made an Electron app or host a meetup, make a pull request to add it to the site and it will make the next roundup.

新的讲座

¥New Talks

9 月,GitHub 举办了 GitHub Universe 大会,该大会被誉为软件未来构建者的盛会。活动中有几场有趣的 Electron 演讲。

¥In September, GitHub held its GitHub Universe conference billed as the event for people building the future of software. There were a couple of interesting Electron talks at the event.

此外,如果你恰好在 12 月 5 日在巴黎,Zeke 将是 在 dotJS 2016 上发表 Electron 演讲

¥Also, if you happen to be in Paris on December 5, Zeke will be giving an Electron talk at dotJS 2016.

新应用

¥New Apps

Pexels搜索完全免费的照片并将其复制到剪贴板
时间戳更好的 macOS 菜单栏时钟,带有可自定义的日期/时间显示和日历。
Harmony音乐播放器兼容 Spotify、Soundcloud、Play Music 和本地文件
uPhoneWebRTC 桌面手机
SealTalk由融云即时通讯云服务和即时通讯 SDK 支持的即时通讯应用
Infinity一种简单的演示方法
Cycligent Git 工具为你的 Git 项目提供直观的图形用户界面
Foco使用 Foco 保持专注并提高生产力
Strawberry赢得终身食客的青睐 使用一体化餐厅软件套件,更好地了解并为他们提供服务。
Mixmax实时查看电子邮件上的每个操作,随时随地撰写。
Firebase AdminFirebase 数据管理工具
ANote简单易用的 Markdown 注释
Temps一款简洁智能的菜单栏天气应用
Amium一款让文件对话的工作协作产品
Soube简单的音乐播放器
(无)彩色下一代桌面富内容编辑器,可保存兼容 HTML 和 Markdown 主题的文档。适用于 Windows、OS X 和 Linux。
quickcalc菜单栏计算器
Forestpin Analytics面向企业的财务数据分析工具
LingREST 客户端
Shorttexts常用文本、文件夹和表情符号的快捷方式
前端盒一组前端代码生成器

Electron 的结构化 API 文档

· 7 min read

今天,我们宣布对 Electron 的文档进行一些改进。现在每个新版本都包含一个 JSON 文件,详细描述 Electron 的所有公共 API。我们创建此文件是为了让开发者能够以有趣的新方式使用 Electron 的 API 文档。

¥Today we're announcing some improvements to Electron's documentation. Every new release now includes a JSON file that describes all of Electron's public APIs in detail. We created this file to enable developers to use Electron's API documentation in interesting new ways.


架构概述

¥Schema overview

每个 API 都是一个对象,具有名称、描述、类型等属性。诸如 BrowserWindowMenu 之类的类具有描述其实例方法、实例属性、实例事件等的附加属性。

¥Each API is an object with properties like name, description, type, etc. Classes such as BrowserWindow and Menu have additional properties describing their instance methods, instance properties, instance events, etc.

以下是描述 BrowserWindow 类的架构摘录:

¥Here's an excerpt from the schema that describes the BrowserWindow class:

{
name: 'BrowserWindow',
description: 'Create and control browser windows.',
process: {
main: true,
renderer: false
},
type: 'Class',
instanceName: 'win',
slug: 'browser-window',
websiteUrl: 'https://electron.nodejs.cn/docs/api/browser-window',
repoUrl: 'https://github.com/electron/electron/blob/v1.4.0/docs/api/browser-window.md',
staticMethods: [...],
instanceMethods: [...],
instanceProperties: [...],
instanceEvents: [...]
}

这是一个方法描述的示例,在本例中是 apis.BrowserWindow.instanceMethods.setMaximumSize 实例方法:

¥And here's an example of a method description, in this case the apis.BrowserWindow.instanceMethods.setMaximumSize instance method:

{
name: 'setMaximumSize',
signature: '(width, height)',
description: 'Sets the maximum size of window to width and height.',
parameters: [{
name: 'width',
type: 'Integer'
}, {
name: 'height',
type: 'Integer'
}]
}

使用新数据

¥Using the new data

为了方便开发者在他们的项目中使用这些结构化数据,我们创建了 electron-docs-api,这是一个小型 npm 包,每当有新的 Electron 版本发布时都会自动发布。

¥To make it easy for developers to use this structured data in their projects, we've created electron-docs-api, a small npm package that is published automatically whenever there's a new Electron release.

npm install electron-api-docs --save

为了立即获得满足,请在你的 Node.js REPL 中试用该模块:

¥For instant gratification, try out the module in your Node.js REPL:

npm i -g trymodule && trymodule electron-api-docs=apis

数据如何收集

¥How the data is collected

Electron 的 API 文档遵循 ​​Electron 编码风格Electron 代码规范,因此其内容可以通过编程方式解析。

¥Electron's API documentation adheres to Electron Coding Style and the Electron Styleguide, so its content can be programmatically parsed.

electron-docs-linterelectron/electron 存储库的新开发依赖。它是一个命令行工具,可以检查所有 Markdown 文件并强制执行样式指南的规则。如果发现错误,则会列出错误并停止发布流程。如果 API 文档有效,则会创建 electron-json.api 文件,并且 已上传至 GitHub 将作为 Electron 版本的一部分。

¥The electron-docs-linter is a new development dependency of the electron/electron repository. It is a command-line tool that lints all the markdown files and enforces the rules of the styleguide. If errors are found, they are listed and the release process is halted. If the API docs are valid, the electron-json.api file is created and uploaded to GitHub as part of the Electron release.

标准 Javascript 和标准 Markdown

¥Standard Javascript and Standard Markdown

今年早些时候,Electron 的代码库已更新,所有 JavaScript 代码均使用 standard 语法检查器。Standard 的 README 总结了这一选择背后的原因:

¥Earlier this year, Electron's codebase was updated to use the standard linter for all JavaScript. Standard's README sums up the reasoning behind this choice:

采用标准风格意味着将代码清晰度和社区约定的重要性置于个人风格之上。这可能并不适用于所有项目和开发文化,但开源对新手来说可能并不友好。设置清晰、自动化的贡献者期望值,使项目更健康。

¥Adopting standard style means ranking the importance of code clarity and community conventions higher than personal style. This might not make sense for 100% of projects and development cultures, however open source can be a hostile place for newbies. Setting up clear, automated contributor expectations makes a project healthier.

我们最近还创建了 standard-markdown,用于验证文档中的所有 JavaScript 代码片段是否有效,并与代码库本身的样式一致。

¥We also recently created standard-markdown to verify that all the JavaScript code snippets in our documentation are valid and consistent with the style in the codebase itself.

这些工具共同帮助我们使用持续集成 (CI) 自动查找拉取请求中的错误。这减轻了人工代码审查的负担,并使我们对文档的准确性更有信心。

¥Together these tools help us use continuous integration (CI) to automatically find errors in pull requests. This reduces the burden placed on humans doing code review, and gives us more confidence about the accuracy of our documentation.

社区成果

¥A community effort

Electron 的文档在不断改进,这要感谢我们优秀的开源社区。截至撰写本文时,已有近 300 人为文档做出了贡献。

¥Electron's documentation is constantly improving, and we have our awesome open-source community to thank for it. As of this writing, nearly 300 people have contributed to the docs.

我们很期待看到人们如何使用这些新的结构化数据。可能的用途包括:

¥We're excited to see what people do with this new structured data. Possible uses include:

Electron 内部机制:弱引用

· 13 min read

作为一种具有垃圾收集功能的语言,JavaScript 让用户无需手动管理资源。但由于 Electron 承载着这个环境,因此必须非常小心地避免内存和资源泄漏。

¥As a language with garbage collection, JavaScript frees users from managing resources manually. But because Electron hosts this environment, it has to be very careful avoiding both memory and resources leaks.

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

¥This post introduces the concept of weak references and how they are used to manage resources in Electron.


弱引用

¥Weak references

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

¥In JavaScript, whenever you assign an object to a variable, you are adding a reference to the object. As long as there is a reference to the object, it will always be kept in memory. Once all references to the object are gone, i.e. there are no longer variables storing the object, the JavaScript engine will recoup the memory on next garbage collection.

弱引用是一种对对象的引用,它允许你获取该对象,而不会影响该对象是否被垃圾回收。当对象被垃圾回收时,你也会收到通知。这样就可以使用 JavaScript 管理资源了。

¥A weak reference is a reference to an object that allows you to get the object without effecting whether it will be garbage collected or not. You will also get notified when the object is garbage collected. It then becomes possible to manage resources with JavaScript.

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

¥Using the NativeImage class in Electron as an example, every time you call the nativeImage.create() API, a NativeImage instance is returned and it is storing the image data in C++. Once you are done with the instance and the JavaScript engine (V8) has garbage collected the object, code in C++ will be called to free the image data in memory, so there is no need for users manage this manually.

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

¥Another example is the window disappearing problem, which visually shows how the window is garbage collected when all the references to it are gone.

在 Electron 中测试弱引用

¥Testing weak references in Electron

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

¥There is no way to directly test weak references in raw JavaScript since the language doesn't have a way to assign weak references. The only API in JavaScript related to weak references is WeakMap, but since it only creates weak-reference keys, it is impossible to know when an object has been garbage collected.

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

¥In versions of Electron prior to v0.37.8, you can use the internal v8Util.setDestructor API to test weak references, which adds a weak reference to the passed object and calls the callback when the object is garbage collected:

// 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 函数。

¥Note that you have to start Electron with the --js-flags="--expose_gc" command switch to expose the internal gc function.

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

¥The API was removed in later versions because V8 actually does not allow running JavaScript code in the destructor and in later versions doing so would cause random crashes.

remote 模块中的弱引用

¥Weak references in the remote module

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

¥Apart from managing native resources with C++, Electron also needs weak references to manage JavaScript resources. An example is Electron's remote module, which is a Remote Procedure Call (RPC) module that allows using objects in the main process from renderer processes.

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

¥One key challenge with the remote module is to avoid memory leaks. When users acquire a remote object in the renderer process, the remote module must guarantee the object continues to live in the main process until the references in the renderer process are gone. Additionally, it also has to make sure the object can be garbage collected when there are no longer any reference to it in renderer processes.

例如,如果没有正确实现,以下代码会很快导致内存泄漏:

¥For example, without proper implementation, following code would cause memory leaks quickly:

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

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

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

¥The resource management in the remote module is simple. Whenever an object is requested, a message is sent to the main process and Electron will store the object in a map and assign an ID for it, then send the ID back to the renderer process. In the renderer process, the remote module will receive the ID and wrap it with a proxy object and when the proxy object is garbage collected, a message will be sent to the main process to free the object.

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

¥Using remote.require API as an example, a simplified implementation looks like this:

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;
};

在主进程中:

¥In the main process:

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];
});

带有弱值的 Map

¥Maps with weak values

在之前的简单实现中,remote 模块中的每次调用都会从主进程返回一个新的远程对象,每个远程对象都代表对主进程中该对象的引用。

¥With the previous simple implementation, every call in the remote module will return a new remote object from the main process, and each remote object represents a reference to the object in the main process.

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

¥The design itself is fine, but the problem is when there are multiple calls to receive the same object, multiple proxy objects will be created and for complicated objects this can add huge pressure on memory usage and garbage collection.

例如,以下代码:

¥For example, the following code:

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

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

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

¥It first uses a lot of memory creating proxy objects and then occupies the CPU (Central Processing Unit) for garbage collecting them and sending IPC messages.

一个显而易见的优化方法是缓存远程对象:如果已存在具有相同 ID 的远程对象,则将返回先前的远程对象,而不是创建新的远程对象。

¥An obvious optimization is to cache the remote objects: when there is already a remote object with the same ID, the previous remote object will be returned instead of creating a new one.

JavaScript 核心中的 API 无法做到这一点。使用普通映射缓存对象可以防止 V8 引擎对对象进行垃圾回收,而 WeakMap 类只能将对象用作弱键。

¥This is not possible with the API in JavaScript core. Using the normal map to cache objects will prevent V8 from garbage collecting the objects, while the WeakMap class can only use objects as weak keys.

为了解决这个问题,添加了一个将值作为弱引用的 map 类型,它非常适合缓存带有 ID 的对象。现在 remote.require 看起来像这样:

¥To solve this, a map type with values as weak references is added, which is perfect for caching objects with IDs. Now the remote.require looks like this:

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 将对象存储为弱引用,因此在对象被垃圾回收时无需删除键。

¥Note that the remoteObjectCache stores objects as weak references, so there is no need to delete the key when the object is garbage collected.

原生代码

¥Native code

如果你对 Electron 中 C++ 弱引用代码感兴趣,可以在以下文件中找到:

¥For people interested in the C++ code of weak references in Electron, it can be found in following files:

setDestructor API:

createIDWeakMap API:

2016 年 8 月:新应用

· 3 min read

以下是 8 月份添加到网站的全新 Electron 应用。

¥Here are the new Electron apps that were added to the site in August.


网站已更新,包含来自社区的新 appsmeetups拉取请求。你可以订阅 查看代码库 以获取新增内容的通知,或者如果你对网站的所有更改不感兴趣,请订阅 博客 RSS 订阅

¥The site is updated with new apps and meetups through pull requests from the community. You can watch the repository to get notifications of new additions or if you're not interested in all of the site's changes, subscribe to the blog RSS feed.

如果你开发过 Electron 应用或举办过聚会,请创建一个 拉取请求 并将其添加到网站,它将出现在下一轮总结中。

¥If you've made an Electron app or host a meetup, make a pull request to add it to the site and it will make the next roundup.

新应用

¥New Apps

Code RPGifyRPG 风格编码应用
PamFax用于发送和接收传真的跨平台应用
BlankUpMarkdown 编辑器清晰度 +1
Rambox将常见的 Web 应用合二为一的免费开源消息和电子邮件应用
Gordie最适合你卡片收藏的应用
Ionic Creator更快地构建出色的移动应用
TwitchAlerts用美观的警报和通知让你的浏览者满意
Museeks一款简洁、干净且跨平台的音乐播放器
SeaPigMarkdown 到 HTML 的转换器
GroupMe非官方 GroupMe 应用
Moeditor你的通用 Markdown 编辑器
SoundnodeSoundnode App 是桌面版的 Soundcloud
QMUI WebQMUI Web Desktop 是一款基于 QMUI Web 框架的项目管理应用。
Svgsus整理、清理和转换你的 SVG
Ramme非官方 Instagram 桌面应用
InsomniaREST API 客户端
Correo适用于 Windows、macOS 和 Linux 的菜单栏/任务栏 Gmail 应用
KongDashKong Admin API 的桌面客户端
翻译编辑器用于 INTL ICU 消息的翻译文件编辑器(参见 formatjsio)
5EClient5EPlay CSGO 客户端
Theme Juice简化本地 WordPress 开发

辅助功能工具

· 4 min read

打造无障碍应用至关重要,我们很高兴为 DevtronSpectron 引入新功能,让开发者有机会让他们的应用更适合所有人。

¥Making accessible applications is important and we're happy to introduce new functionality to Devtron and Spectron that gives developers the opportunity to make their apps better for everyone.


Electron 应用中的可访问性问题与网站的可访问性问题类似,因为它们最终都是 HTML。然而,对于 Electron 应用,你无法使用在线资源进行可访问性审核,因为你的应用没有 URL 指向审核员。

¥Accessibility concerns in Electron applications are similar to those of websites because they're both ultimately HTML. With Electron apps, however, you can't use the online resources for accessibility audits because your app doesn't have a URL to point the auditor to.

这些新功能将这些审计工具引入你的 Electron 应用。你可以选择使用 Spectron 为你的测试添加审计,或者在 DevTools 中使用 Devtron 的审计功能。继续阅读工具摘要或查看我们的 无障碍文档 了解更多信息。

¥These new features bring those auditing tools to your Electron app. You can choose to add audits to your tests with Spectron or use them within DevTools with Devtron. Read on for a summary of the tools or checkout our accessibility documentation for more information.

Spectron

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

¥In the testing framework Spectron, you can now audit each window and <webview> tag in your application. For example:

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

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

¥You can read more about this feature in Spectron's documentation.

Devtron

Devtron 中有一个新的辅助功能选项卡,可让你审核页面在你的应用中,对结果进行排序和筛选。

¥In Devtron there is a new accessibility tab which will allow you to audit a page in your app, sort and filter the results.

devtron screenshot

这两个工具都使用了 Google 为 Chrome 构建的 辅助功能开发者工具 库。你可以了解更多关于此库在 代码库的 wiki 上使用的可访问性审核规则的信息。

¥Both of these tools are using the Accessibility Developer Tools library built by Google for Chrome. You can learn more about the accessibility audit rules this library uses on that repository's wiki.

如果你知道其他适用于 Electron 的优秀辅助工具,请通过拉取请求将它们添加到 无障碍文档

¥If you know of other great accessibility tools for Electron, add them to the accessibility documentation with a pull request.

npm 安装 electron

· 6 min read

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

¥As of Electron version 1.3.1, you can npm install electron --save-dev to install the latest precompiled version of Electron in your app.


npm install electron

预构建的 Electron 二进制文件

¥The prebuilt Electron binary

如果你之前开发过 Electron 应用,那么你可能接触过 electron-prebuilt 这个 npm 软件包。此软件包几乎是每个 Electron 项目不可或缺的一部分。安装后,它会检测你的操作系统并下载一个预先编译好的二进制文件,该二进制文件已编译并可在你的系统架构上运行。

¥If you've ever worked on an Electron app before, you've likely come across the electron-prebuilt npm package. This package is an indispensable part of nearly every Electron project. When installed, it detects your operating system and downloads a prebuilt binary that is compiled to work on your system's architecture.

新名称

¥The new name

Electron 的安装过程常常是新开发者的绊脚石。许多勇敢的人尝试通过运行 npm install electron 而不是 npm install electron-prebuilt 来开始开发 Electron 应用,结果却发现(通常是在经过一番困惑之后)这并不是他们想要的 electron

¥The Electron installation process was often a stumbling block for new developers. Many brave people tried to get started developing an Electron by app by running npm install electron instead of npm install electron-prebuilt, only to discover (often after much confusion) that it was not the electron they were looking for.

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

¥This was because there was an existing electron project on npm, created before GitHub's Electron project existed. To help make Electron development easier and more intuitive for new developers, we reached out to the owner of the existing electron npm package to ask if he'd be willing to let us use the name. Luckily he was a fan of our project, and agreed to help us repurpose the name.

预构建版本仍在运行

¥Prebuilt lives on

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

¥As of version 1.3.1, we have begun publishing electron and electron-prebuilt packages to npm in tandem. The two packages are identical. We chose to continue publishing the package under both names for a while so as not to inconvenience the thousands of developers who are currently using electron-prebuilt in their projects. We recommend updating your package.json files to use the new electron dependency, but we will continue releasing new versions of electron-prebuilt until the end of 2016.

electron-userland/electron-prebuilt 仓库将继续作为 electron npm 软件包的规范存储地。

¥The electron-userland/electron-prebuilt repository will remain the canonical home of the electron npm package.

非常感谢

¥Many thanks

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

¥We owe a special thanks to @mafintosh, @maxogden, and many other contributors for creating and maintaining electron-prebuilt, and for their tireless service to the JavaScript, Node.js, and Electron communities.

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

¥And thanks to @logicalparadox for allowing us to take over the electron package on npm.

更新你的项目

¥Updating your projects

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

¥We've worked with the community to update popular packages that are affected by this change. Packages like electron-packager, electron-rebuild, and electron-builder have already been updated to work with the new name while continuing to support the old name.

如果你在安装此新软件包时遇到任何问题,请在 electron-userland/electron-prebuilt 代码库中提交问题告知我们。

¥If you encounter any problems installing this new package, please let us know by opening an issue on the electron-userland/electron-prebuilt repository.

如需解决 Electron 的任何其他问题,请使用 electron/electron 代码库。

¥For any other issues with Electron, please use the electron/electron repository.

Electron 内部机制:使用 Node 作为库

· 10 min read

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

¥This is the second post in an ongoing series explaining the internals of Electron. Check out the first post about event loop integration if you haven't already.

大多数人将 节点 用于服务器端应用,但由于 Node 拥有丰富的 API 集和蓬勃发展的社区,它也非常适合用作嵌入式库。本文解释了如何在 Electron 中将 Node 用作库。

¥Most people use Node for server-side applications, but because of Node's rich API set and thriving community, it is also a great fit for an embedded library. This post explains how Node is used as a library in Electron.


构建系统

¥Build system

Node 和 Electron 都使用 GYP 作为构建系统。如果你想在应用中嵌入 Node,你也必须将其用作构建系统。

¥Both Node and Electron use GYP as their build systems. If you want to embed Node inside your app, you have to use it as your build system too.

GYP 新手?在继续阅读本文之前,请先阅读 本指南

¥New to GYP? Read this guide before you continue further in this post.

Node 的标志

¥Node's flags

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

¥The node.gyp file in Node's source code directory describes how Node is built, along with lots of GYP variables controlling which parts of Node are enabled and whether to open certain configurations.

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

¥To change the build flags, you need to set the variables in the .gypi file of your project. The configure script in Node can generate some common configurations for you, for example running ./configure --shared will generate a config.gypi with variables instructing Node to be built as a shared library.

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

¥Electron does not use the configure script since it has its own build scripts. The configurations for Node are defined in the common.gypi file in Electron's root source code directory.

¥Link Node with Electron

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

¥In Electron, Node is being linked as a shared library by setting the GYP variable node_shared to true, so Node's build type will be changed from executable to shared_library, and the source code containing the Node's main entry point will not be compiled.

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

¥Since Electron uses the V8 library shipped with Chromium, the V8 library included in Node's source code is not used. This is done by setting both node_use_v8_platform and node_use_bundled_v8 to false.

共享库或静态库

¥Shared library or static library

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

¥When linking with Node, there are two options: you can either build Node as a static library and include it in the final executable, or you can build it as a shared library and ship it alongside the final executable.

在 Electron 中,Node 长期以来都是作为静态库构建的。这使得构建变得简单,启用了最佳的编译器优化,并且允许 Electron 无需额外的 node.dll 文件即可分发。

¥In Electron, Node was built as a static library for a long time. This made the build simple, enabled the best compiler optimizations, and allowed Electron to be distributed without an extra node.dll file.

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

¥However, this changed after Chrome switched to use BoringSSL. BoringSSL is a fork of OpenSSL that removes several unused APIs and changes many existing interfaces. Because Node still uses OpenSSL, the compiler would generate numerous linking errors due to conflicting symbols if they were linked together.

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

¥Electron couldn't use BoringSSL in Node, or use OpenSSL in Chromium, so the only option was to switch to building Node as a shared library, and hide the BoringSSL and OpenSSL symbols in the components of each.

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

¥This change brought Electron some positive side effects. Before this change, you could not rename the executable file of Electron on Windows if you used native modules because the name of the executable was hard coded in the import library. After Node was built as a shared library, this limitation was gone because all native modules were linked to node.dll, whose name didn't need to be changed.

支持原生模块

¥Supporting native modules

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

¥Native modules in Node work by defining an entry function for Node to load, and then searching the symbols of V8 and libuv from Node. This is a bit troublesome for embedders because by default the symbols of V8 and libuv are hidden when building Node as a library and native modules will fail to load because they cannot find the symbols.

因此,为了让原生模块正常工作,V8 和 libuv 符号在 Electron 中被公开。对于 V8,此操作由 强制公开 Chromium 配置文件中的所有符号 完成。对于 libuv,它由 设置 BUILDING_UV_SHARED=1 定义 实现。

¥So in order to make native modules work, the V8 and libuv symbols were exposed in Electron. For V8 this is done by forcing all symbols in Chromium's configuration file to be exposed. For libuv, it is achieved by setting the BUILDING_UV_SHARED=1 definition.

在你的应用中启动 Node

¥Starting Node in your app

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

¥After all the work of building and linking with Node, the final step is to run Node in your app.

Node 不提供太多用于嵌入到其他应用中的公共 API。通常,只需调用 node::Startnode::Init 即可启动一个新的 Node 实例。然而,如果你正在基于 Node 构建一个复杂的应用,则必须使用像 node::CreateEnvironment 这样的 API 来精确控制每个步骤。

¥Node doesn't provide many public APIs for embedding itself into other apps. Usually, you can just call node::Start and node::Init to start a new instance of Node. However, if you are building a complex app based on Node, you have to use APIs like node::CreateEnvironment to precisely control every step.

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

¥In Electron, Node is started in two modes: the standalone mode that runs in the main process, which is similar to official Node binaries, and the embedded mode which inserts Node APIs into web pages. The details of this will be explained in a future post.

2016 年 7 月:新应用和聚会

· 3 min read

我们将开始每月汇总 Electron 社区的动态。每个综述都会介绍新应用、即将举行的聚会、工具、视频等内容。

¥We're starting a monthly roundup to highlight activity in the Electron community. Each roundup will feature things like new apps, upcoming meetups, tools, videos, etc.


本网站已更新来自社区的新 appsmeetups拉取请求。你可以订阅 查看代码库 以获取新增内容的通知,或者如果你对网站的所有更改不感兴趣,请订阅 博客 RSS 订阅

¥This site is updated with new apps and meetups through pull requests from the community. You can watch the repository to get notifications of new additions or if you're not interested in all of the site's changes, subscribe to the blog RSS feed.

如果你开发过 Electron 应用或举办过聚会,请创建一个 拉取请求 并将其添加到网站,它将出现在下一轮总结中。

¥If you've made an Electron app or host a meetup, make a pull request to add it to the site and it will make the next roundup.

新应用

¥New Apps

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 播放列表的新方式

新的聚会

¥New Meetups

Electron 开源桌面框架英国伦敦

Electron 内部机制:消息循环集成

· 8 min read

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

¥This is the first post of a series that explains the internals of Electron. This post introduces how Node's event loop is integrated with Chromium in Electron.


曾有多次尝试使用 Node 进行 GUI 编程,例如用于 GTK+ 绑定的 node-gui 和用于 QT 绑定的 node-qt。但它们都无法在生产环境中运行,因为 GUI 工具包有自己的消息循环,而 Node 使用 libuv 作为自己的事件循环,并且主线程一次只能运行一个循环。在 Node 中运行 GUI 消息循环的常用技巧是使用间隔非常短的计时器来触发消息循环,这会导致 GUI 界面响应缓慢并占用大量 CPU 资源。

¥There had been many attempts to use Node for GUI programming, like node-gui for GTK+ bindings, and node-qt for QT bindings. But none of them work in production because GUI toolkits have their own message loops while Node uses libuv for its own event loop, and the main thread can only run one loop at the same time. So the common trick to run GUI message loop in Node is to pump the message loop in a timer with very small interval, which makes GUI interface response slow and occupies lots of CPU resources.

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

¥During the development of Electron we met the same problem, though in a reversed way: we had to integrate Node's event loop into Chromium's message loop.

主进程和渲染进程

¥The main process and renderer process

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

¥Before we dive into the details of message loop integration, I'll first explain the multi-process architecture of Chromium.

Electron 中有两种类型的进程:主进程和渲染进程(这实际上已经极其简化,完整视图请参见 多进程架构)。主进程负责 GUI 工作,例如创建窗口,而渲染进程仅负责运行和渲染网页。

¥In Electron there are two types of processes: the main process and the renderer process (this is actually extremely simplified, for a complete view please see Multi-process Architecture). The main process is responsible for GUI work like creating windows, while the renderer process only deals with running and rendering web pages.

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

¥Electron allows using JavaScript to control both the main process and renderer process, which means we have to integrate Node into both processes.

用 libuv 替换 Chromium 的消息循环

¥Replacing Chromium's message loop with libuv

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

¥My first try was reimplementing Chromium's message loop with libuv.

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

¥It was easy for the renderer process, since its message loop only listened to file descriptors and timers, and I only needed to implement the interface with libuv.

但是,这对于主进程来说要困难得多。每个平台都有自己的 GUI 消息循环。macOS Chromium 使用 NSRunLoop,而 Linux 使用 glib。我尝试了很多方法,从原生 GUI 消息循环中提取底层文件描述符,然后将它们传递给 libuv 进行迭代,但仍然遇到了一些无效的极端情况。

¥However it was significantly more difficult for the main process. Each platform has its own kind of GUI message loops. macOS Chromium uses NSRunLoop, whereas Linux uses glib. I tried lots of hacks to extract the underlying file descriptors out of the native GUI message loops, and then fed them to libuv for iteration, but I still met edge cases that did not work.

因此,我最终添加了一个计时器,用于在短时间间隔内轮询 GUI 消息循环。因此,该进程会持续占用 CPU,并且某些操作会长时间延迟。

¥So finally I added a timer to poll the GUI message loop in a small interval. As a result the process took a constant CPU usage, and certain operations had long delays.

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

¥Polling Node's event loop in a separate thread

随着 libuv 的成熟,我们开始采用另一种方法。

¥As libuv matured, it was then possible to take another approach.

libuv 中引入了后端 fd 的概念,它是一个文件描述符(或句柄),libuv 会轮询它以进行事件循环。因此,通过轮询后端文件描述符 (fd),可以在 libuv 中出现新事件时收到通知。

¥The concept of backend fd was introduced into libuv, which is a file descriptor (or handle) that libuv polls for its event loop. So by polling the backend fd it is possible to get notified when there is a new event in libuv.

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

¥So in Electron I created a separate thread to poll the backend fd, and since I was using the system calls for polling instead of libuv APIs, it was thread safe. And whenever there was a new event in libuv's event loop, a message would be posted to Chromium's message loop, and the events of libuv would then be processed in the main thread.

通过这种方式,我避免了同时修补 Chromium 和 Node,并且主进程和渲染进程都使用了相同的代码。

¥In this way I avoided patching Chromium and Node, and the same code was used in both the main and renderer processes.

代码

¥The code

你可以在 electron/atom/common/ 下的 node_bindings 文件中找到消息循环集成的实现。它可以轻松地复用到想要集成 Node 的项目。

¥You can find the implemention of the message loop integration in the node_bindings files under electron/atom/common/. It can be easily reused for projects that want to integrate Node.

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

¥Update: Implementation moved to electron/shell/common/node_bindings.cc.