Skip to main content

2016年9月:新应用

· 5 min read

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

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


该网站通过社区的拉取请求更新新的应用聚会。你可以关注仓库以接收新内容的通知,或者如果你对网站的所有更改不感兴趣,可以订阅博客 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

九月份,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 的演讲(链接 https://twitter.com/dotJS/status/783615732307333120)。

🌐 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由融云 IM 云服务和 IM SDK 提供支持的即时通讯应用
无限制作演示文稿的简单方法
Cycligent Git 工具为你的 Git 项目提供简单直观的图形界面
Foco保持专注,提高生产力与 Foco
草莓赢取终身餐饮优惠 通过一体化餐饮软件套件更好地了解和服务他们。
Mixmax实时查看你电子邮件的每一个操作 随时随地撰写邮件。
Firebase 管理员一款 Firebase 数据管理工具
ANote一个简单友好的 Markdown 注意
Temps一个简单但智能的菜单栏天气应用
Amium一款将对话带入你的文件的工作协作产品
Soube简单的音乐播放器
(未)着色下一代桌面丰富内容编辑器,可将文档保存为带主题的 HTML 和 Markdown 格式。适用于 Windows、OS X 和 Linux。
quickcalc菜单栏计算器
Forestpin Analytics面向企业的财务数据分析工具
LingREST 客户端
Shortexts你经常复制的文本、文件夹和表情符号的快捷方式
前端框架前端代码生成器集合

作为结构化数据的 Electron API 文档

· 6 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 文件,并作为 Electron 发布的一部分上传到 GitHub

🌐 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 linter。Standard 的自述文件总结了选择这一做法的原因:

🌐 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:

采用标准风格意味着将代码清晰度和社区约定的重要性置于个人风格之上。这可能并不适用于100%的项目和开发文化,但开源社区对新手来说可能是一个充满挑战的环境。建立明确、自动化的贡献者预期可以让项目更加健康。

我们最近还创建了 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 内部机制:弱引用

· 11 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 中与弱引用相关的唯一 API 是 弱映射,但由于它只创建弱引用键,因此无法知道对象何时被垃圾回收。

🌐 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.

在 v0.37.8 之前的 Electron 版本中,你可以使用内部 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 这是不可能的。使用普通的 map 来缓存对象会阻止 V8 对这些对象进行垃圾回收,而 弱映射 类只能将对象作为弱键使用。

🌐 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.

为了解决这个问题,添加了一种值为弱引用的映射类型,这对于缓存具有 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:

🌐 The setDestructor API:

createIDWeakMap API:

🌐 The createIDWeakMap API:

2016年8月:新应用

· 4 min read

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

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


该网站通过社区的拉取请求更新新的应用聚会。你可以关注该代码库以获取新内容的通知,或者如果你对网站的所有更改不感兴趣,可以订阅博客 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

代码 RPG 化RPG 风格编码应用
PamFax一个用于发送和接收传真 的跨平台应用
BlankUp清晰度 +1 的 Markdown 编辑器
Rambox免费且开源的消息和电子邮件应用,将常见的网络应用整合到一个应用中
Gordie适合你的卡片收藏的最佳应用
Ionic Creator更快地构建惊人的移动应用
TwitchAlerts用漂亮的提醒和通知让你的观众满意
Museeks一个简单、干净且跨平台的音乐播放器
SeaPig一个将 Markdown 转换为 HTML 的转换器
GroupMe非官方 GroupMe 应用
Moeditor你的一站式 Markdown 编辑器
SoundnodeSoundnode 应用是桌面版的 Soundcloud
QMUI WebQMUI Web 桌面端是一个用于基于 QMUI Web 框架管理项目的应用
Svgsus组织、清理并转换你的 SVG 文件
Ramme非官方 Instagram 桌面应用
失眠REST API 客户端
Correo一个适用于 Windows、macOS 和 Linux 的 Gmail 菜单栏/任务栏应用
KongDashKong 管理 API 的桌面客户端
翻译编辑器用于 INTL ICU 消息的翻译文件编辑器(参见 formatjsio)
5EClient5EPlay CSGO 客户端
主题汁本地 WordPress 开发变得简单

无障碍工具

· 3 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 构建的 Accessibility Developer Tools 库。你可以在该 仓库的 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

· 5 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.

这是因为在 GitHub 的 Electron 项目存在之前,npm 上已经有一个现存的 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 依赖,但我们将在 2016 年底之前继续发布 electron-prebuilt 的新版本。

🌐 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 以及许多其他 贡献者 创建和维护 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 用作库

· 9 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 开发服务器端应用,但由于 Node 拥有丰富的 API 集合和繁荣的社区,它也非常适合作为嵌入式库使用。本文解释了 Node 是如何在 Electron 中作为库使用的。

🌐 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 的符号是隐藏的,本地模块将无法加载,因为它们找不到这些符号。

因此,为了使本地模块能够工作,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.


该网站通过社区的拉取请求更新新的应用聚会。你可以关注仓库以接收新内容的通知,或者如果你对网站的所有更改不感兴趣,可以订阅博客 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使用网络技术让你构建惊人移动应用的开源框架
WordMark一个为 Markdown 写作者设计的轻量级博客发布编辑器
UbAuth帮助开发者为 Uber 应用使用 OAuth 2.0 创建访问令牌的应用
HyperTermHTML/JS/CSS 终端
MarpMarkdown 演示文稿编写器
Glyphr Studio一个免费的、基于网页的字体设计工具,专注于为爱好者设计字体
BitCrypt一个简单的Windows文件加密应用 加密你的比特
Trym适用于 macOS 的漂亮小应用,帮助你查看、优化和转换 SVG 图标
Booker拥有 Markdown 功能的文本编辑器
PhonePresenter最聪明的演示遥控器
Yout在桌面上观看你在 YouTube 上的播放列表的新方式

新的聚会

🌐 New Meetups

Electron 开源桌面框架英国伦敦

Electron 内部机制:消息循环整合

· 7 min read

这是一个系列文章的第一篇,解释了 Electron 的内部机制。这篇文章介绍了 Node 的事件循环如何与 Electron 中的 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+ 绑定的 节点图形用户界面,以及用于 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 中,它是一个文件描述符(或句柄),libuv 会轮询它以进行事件循环。因此,通过轮询后台文件描述符,可以在 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 中,我创建了一个单独的线程来轮询后端文件描述符,并且由于我使用的是系统调用进行轮询而不是 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.

Electron 播客

· 2 min read

想了解 Electron 吗?有两期新的播客刚刚发布,它们很好地概述了 Electron 是什么、为什么要开发它以及它是如何被使用的。

🌐 Looking for an introduction to Electron? Two new podcasts have just been released that give a great overview of what it is, why it was built, and how it is being used.


现已推出:

Hanselminutes:创建跨平台的 Electron 应用

🌐 Hanselminutes: Creating cross-platform Electron apps

Electron只是“框架里的Chrome”吗,还是它远不止如此?Jessica为Scott指明了正确的方向,并详细解释了Electron平台在你的开发世界中到底扮演什么角色。


JavaScript Air:Electron 应用

🌐 JavaScript Air: Electron Apps

Electron 正变得越来越受欢迎,成为使用网页技术构建跨平台桌面应用的一种重要方式。让我们深入了解这项出色的技术,看看如何利用它来提升我们自己的体验以及用户在桌面上的使用体验。


如果你想了解 Electron 的入门知识,可以先听第一部分。第二部分则更详细地介绍了如何构建应用,并提供了 Nylas 的 Evan Morikawa 的一些实用技巧。

🌐 If you're looking for an introduction to Electron, give the first a listen. The second goes into more detail about building apps with great tips from Nylas's Evan Morikawa.

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

🌐 We are currently working on two more podcasts that should come out next month, keep an eye on the @ElectronJS Twitter account for updates.