性能
¥Performance
开发者经常询问优化 Electron 应用性能的策略。软件工程师、消费者和框架开发者并不总是就 "performance" 的含义达成一致。本文档概述了 Electron 维护者最喜欢的一些方法,以减少内存、CPU 和磁盘资源的使用量,同时确保你的应用响应用户输入并尽快完成操作。此外,我们希望所有性能策略都能保持应用安全的高标准。
¥Developers frequently ask about strategies to optimize the performance of Electron applications. Software engineers, consumers, and framework developers do not always agree on one single definition of what "performance" means. This document outlines some of the Electron maintainers' favorite ways to reduce the amount of memory, CPU, and disk resources being used while ensuring that your app is responsive to user input and completes operations as quickly as possible. Furthermore, we want all performance strategies to maintain a high standard for your app's security.
有关如何使用 JavaScript 构建高性能网站的智慧和信息通常也适用于 Electron 应用。在某种程度上,讨论如何构建高性能 Node.js 应用的资源也适用,但请注意,术语 "performance" 对于 Node.js 后端的含义与对于在客户端上运行的应用的含义不同。
¥Wisdom and information about how to build performant websites with JavaScript generally applies to Electron apps, too. To a certain extent, resources discussing how to build performant Node.js applications also apply, but be careful to understand that the term "performance" means different things for a Node.js backend than it does for an application running on a client.
此列表是为了你的方便而提供的,并且与我们的 安全检查表 非常相似,并不意味着详尽无遗。可能可以构建一个遵循下面列出的所有步骤的缓慢的 Electron 应用。Electron 是一个强大的开发平台,使你(开发者)能够或多或少地做任何你想做的事情。所有这些自由意味着性能很大程度上是你的责任。
¥This list is provided for your convenience – and is, much like our security checklist – not meant to be exhaustive. It is probably possible to build a slow Electron app that follows all the steps outlined below. Electron is a powerful development platform that enables you, the developer, to do more or less whatever you want. All that freedom means that performance is largely your responsibility.
测量,测量,测量
¥Measure, Measure, Measure
下面的列表包含许多相当简单且易于实现的步骤。但是,构建应用的最高性能版本将需要你执行许多步骤。相反,你必须通过仔细分析和测量来仔细检查应用中运行的所有代码。瓶颈在哪里?当用户单击按钮时,哪些操作占据了主要时间?当应用只是闲置时,哪些对象占用最多内存?
¥The list below contains a number of steps that are fairly straightforward and easy to implement. However, building the most performant version of your app will require you to go beyond a number of steps. Instead, you will have to closely examine all the code running in your app by carefully profiling and measuring. Where are the bottlenecks? When the user clicks a button, what operations take up the brunt of the time? While the app is simply idling, which objects take up the most memory?
我们一次又一次地看到,构建高性能 Electron 应用的最成功策略是分析正在运行的代码,找到其中最耗资源的部分,然后对其进行优化。一遍又一遍地重复这个看似费力的过程将极大地提高应用的性能。使用 Visual Studio Code 或 Slack 等主要应用的经验表明,这种做法是迄今为止提高性能的最可靠策略。
¥Time and time again, we have seen that the most successful strategy for building a performant Electron app is to profile the running code, find the most resource-hungry piece of it, and to optimize it. Repeating this seemingly laborious process over and over again will dramatically increase your app's performance. Experience from working with major apps like Visual Studio Code or Slack has shown that this practice is by far the most reliable strategy to improve performance.
要详细了解如何分析应用的代码,请熟悉 Chrome 开发者工具。对于同时查看多个进程的高级分析,请考虑使用 Chrome 追踪 工具。
¥To learn more about how to profile your app's code, familiarize yourself with the Chrome Developer Tools. For advanced analysis looking at multiple processes at once, consider the Chrome Tracing tool.
推荐阅读
¥Recommended Reading
清单:性能建议
¥Checklist: Performance recommendations
如果你尝试这些步骤,你的应用可能会更精简、更快,并且通常对资源的消耗更少。
¥Chances are that your app could be a little leaner, faster, and generally less resource-hungry if you attempt these steps.
-
当你不需要默认菜单时,请调用
Menu.setApplicationMenu(null)
¥Call
Menu.setApplicationMenu(null)
when you do not need a default menu
1. 不小心包含模块
¥ Carelessly including modules
在将 Node.js 模块添加到应用之前,请检查该模块。该模块包含多少个依赖?简单的在 require()
语句中调用需要什么样的资源呢?你可能会发现,NPM 包注册表中下载次数最多的模块或 GitHub 上星星最多的模块实际上并不是可用的最精简或最小的模块。
¥Before adding a Node.js module to your application, examine said module. How
many dependencies does that module include? What kind of resources does
it need to simply be called in a require()
statement? You might find
that the module with the most downloads on the NPM package registry or the most stars on GitHub
is not in fact the leanest or smallest one available.
为什么?
¥Why?
这个建议背后的推断最好用一个现实世界的例子来说明。在 Electron 的早期,网络连接的可靠检测是一个问题,导致许多应用使用公开简单 isOnline()
方法的模块。
¥The reasoning behind this recommendation is best illustrated with a real-world
example. During the early days of Electron, reliable detection of network
connectivity was a problem, resulting in many apps using a module that exposed a
simple isOnline()
method.
该模块通过尝试连接多个已知端点来检测你的网络连接。对于这些端点的列表,它取决于不同的模块,该模块还包含众所周知的端口列表。这种依赖本身依赖于一个包含端口信息的模块,该模块以 JSON 文件的形式出现,内容超过 100,000 行。每当加载模块时(通常在 require('module')
语句中),它都会加载其所有依赖并最终读取并解析此 JSON 文件。解析数千行 JSON 是一项非常昂贵的操作。在慢速机器上,它可能会花费整整几秒钟的时间。
¥That module detected your network connectivity by attempting to reach out to a
number of well-known endpoints. For the list of those endpoints, it depended on
a different module, which also contained a list of well-known ports. This
dependency itself relied on a module containing information about ports, which
came in the form of a JSON file with more than 100,000 lines of content.
Whenever the module was loaded (usually in a require('module')
statement),
it would load all its dependencies and eventually read and parse this JSON
file. Parsing many thousands lines of JSON is a very expensive operation. On
a slow machine it can take up whole seconds of time.
在许多服务器环境中,启动时间实际上是无关紧要的。如果 Node.js 服务器在服务器启动时将所有必需的信息加载到内存中,从而可以更快地处理请求,则需要有关所有端口的信息的 Node.js 服务器实际上可能是 "性能更高"。本示例中讨论的模块不是 "bad" 模块。然而,Electron 应用不应该在内存中加载、解析和存储它实际上不需要的信息。
¥In many server contexts, startup time is virtually irrelevant. A Node.js server that requires information about all ports is likely actually "more performant" if it loads all required information into memory whenever the server boots at the benefit of serving requests faster. The module discussed in this example is not a "bad" module. Electron apps, however, should not be loading, parsing, and storing in memory information that it does not actually need.
简而言之,主要为运行 Linux 的 Node.js 服务器编写的看似优秀的模块可能对应用的性能来说是个坏消息。在这个特定的示例中,正确的解决方案是根本不使用任何模块,而是使用 Chromium 更高版本中包含的连接检查。
¥In short, a seemingly excellent module written primarily for Node.js servers running Linux might be bad news for your app's performance. In this particular example, the correct solution was to use no module at all, and to instead use connectivity checks included in later versions of Chromium.
如何?
¥How?
在考虑模块时,我们建议你检查:
¥When considering a module, we recommend that you check:
-
包含的依赖的大小
¥the size of dependencies included
-
加载 (
require()
) 所需的资源¥the resources required to load (
require()
) it -
执行你感兴趣的操作所需的资源
¥the resources required to perform the action you're interested in
可以使用命令行上的单个命令来生成用于加载模块的 CPU 配置文件和堆内存配置文件。在下面的示例中,我们正在查看流行的模块 request
。
¥Generating a CPU profile and a heap memory profile for loading a module can be done
with a single command on the command line. In the example below, we're looking at
the popular module request
.
node --cpu-prof --heap-prof -e "require('request')"
执行此命令会在执行该命令的目录中生成 .cpuprofile
文件和 .heapprofile
文件。这两个文件都可以使用 Chrome 开发者工具(分别使用 Performance
和 Memory
选项卡)进行分析。
¥Executing this command results in a .cpuprofile
file and a .heapprofile
file in the directory you executed it in. Both files can be analyzed using
the Chrome Developer Tools, using the Performance
and Memory
tabs
respectively.
在此示例中,在作者的计算机上,我们看到加载 request
花费了近半秒,而 node-fetch
占用的内存显着减少,不到 50 毫秒。
¥In this example, on the author's machine, we saw that loading request
took
almost half a second, whereas node-fetch
took dramatically less memory
and less than 50ms.
2. 加载和运行代码太快
¥ Loading and running code too soon
如果你有昂贵的设置操作,请考虑推迟这些操作。检查应用启动后立即执行的所有工作。不要立即启动所有操作,而是考虑按照与用户旅程更紧密一致的顺序交错执行这些操作。
¥If you have expensive setup operations, consider deferring those. Inspect all the work being executed right after the application starts. Instead of firing off all operations right away, consider staggering them in a sequence more closely aligned with the user's journey.
在传统的 Node.js 开发中,我们习惯将所有 require()
语句放在顶部。如果你当前正在使用相同的策略编写 Electron 应用,并且正在使用你并不立即需要的相当大的模块,请应用相同的策略并将加载推迟到更合适的时间。
¥In traditional Node.js development, we're used to putting all our require()
statements at the top. If you're currently writing your Electron application
using the same strategy and are using sizable modules that you do not
immediately need, apply the same strategy and defer loading to a more
opportune time.
为什么?
¥Why?
加载模块是一项非常昂贵的操作,尤其是在 Windows 上。当你的应用启动时,它不应该让用户等待当前不需要的操作。
¥Loading modules is a surprisingly expensive operation, especially on Windows. When your app starts, it should not make users wait for operations that are currently not necessary.
这似乎是显而易见的,但许多应用往往会在应用启动后立即执行大量工作 - 例如检查更新、下载稍后流程中使用的内容或执行大量磁盘 I/O 操作。
¥This might seem obvious, but many applications tend to do a large amount of work immediately after the app has launched - like checking for updates, downloading content used in a later flow, or performing heavy disk I/O operations.
让我们以 Visual Studio Code 为例。当你打开文件时,它会立即向你显示该文件,而不会高亮任何代码,从而优先考虑你与文本交互的能力。一旦完成该工作,它将继续进行代码高亮。
¥Let's consider Visual Studio Code as an example. When you open a file, it will immediately display the file to you without any code highlighting, prioritizing your ability to interact with the text. Once it has done that work, it will move on to code highlighting.
如何?
¥How?
让我们考虑一个示例,假设你的应用正在解析虚构的 .foo
格式的文件。为了做到这一点,它依赖于同样虚构的 foo-parser
模块。在传统的 Node.js 开发中,你可能会编写预加载依赖的代码:
¥Let's consider an example and assume that your application is parsing files
in the fictitious .foo
format. In order to do that, it relies on the
equally fictitious foo-parser
module. In traditional Node.js development,
you might write code that eagerly loads dependencies:
const fs = require('node:fs')
const fooParser = require('foo-parser')
class Parser {
constructor () {
this.files = fs.readdirSync('.')
}
getParsedFiles () {
return fooParser.parse(this.files)
}
}
const parser = new Parser()
module.exports = { parser }
在上面的示例中,我们正在执行大量工作,这些工作在文件加载后立即执行。我们需要立即获取解析后的文件吗?我们可以稍后再做这项工作,当 getParsedFiles()
实际被调用时吗?
¥In the above example, we're doing a lot of work that's being executed as soon
as the file is loaded. Do we need to get parsed files right away? Could we
do this work a little later, when getParsedFiles()
is actually called?
// "fs" is likely already being loaded, so the `require()` call is cheap
const fs = require('node:fs')
class Parser {
async getFiles () {
// Touch the disk as soon as `getFiles` is called, not sooner.
// Also, ensure that we're not blocking other operations by using
// the asynchronous version.
this.files = this.files || await fs.promises.readdir('.')
return this.files
}
async getParsedFiles () {
// Our fictitious foo-parser is a big and expensive module to load, so
// defer that work until we actually need to parse files.
// Since `require()` comes with a module cache, the `require()` call
// will only be expensive once - subsequent calls of `getParsedFiles()`
// will be faster.
const fooParser = require('foo-parser')
const files = await this.getFiles()
return fooParser.parse(files)
}
}
// This operation is now a lot cheaper than in our previous example
const parser = new Parser()
module.exports = { parser }
简而言之,分配资源 "恰逢其时",而不是在应用启动时分配所有资源。
¥In short, allocate resources "just in time" rather than allocating them all when your app starts.
3. 阻塞主进程
¥ Blocking the main process
Electron 的主进程(有时称为 "浏览器进程")比较特殊:它是应用所有其他进程的父进程,也是操作系统与之交互的主进程。它处理窗口、交互以及应用内各个组件之间的通信。它还包含 UI 线程。
¥Electron's main process (sometimes called "browser process") is special: It is the parent process to all your app's other processes and the primary process the operating system interacts with. It handles windows, interactions, and the communication between various components inside your app. It also houses the UI thread.
在任何情况下都不应使用长时间运行的操作来阻止此进程和 UI 线程。阻塞 UI 线程意味着整个应用将冻结,直到主进程准备好继续处理。
¥Under no circumstances should you block this process and the UI thread with long-running operations. Blocking the UI thread means that your entire app will freeze until the main process is ready to continue processing.
为什么?
¥Why?
主进程及其 UI 线程本质上是应用内主要操作的控制塔。当操作系统告诉你的应用有关鼠标单击的信息时,它将在到达你的窗口之前执行主进程。如果你的窗口正在渲染极其平滑的动画,则需要与 GPU 进程进行相关操作 - 再次执行主进程。
¥The main process and its UI thread are essentially the control tower for major operations inside your app. When the operating system tells your app about a mouse click, it'll go through the main process before it reaches your window. If your window is rendering a buttery-smooth animation, it'll need to talk to the GPU process about that – once again going through the main process.
Electron 和 Chromium 会小心地将大量磁盘 I/O 和 CPU 密集型操作放到新线程上,以避免阻塞 UI 线程。你也应该这样做。
¥Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations onto new threads to avoid blocking the UI thread. You should do the same.
如何?
¥How?
Electron 强大的多进程架构随时可以帮助你完成长时间运行的任务,但也包括少量的性能陷阱。
¥Electron's powerful multi-process architecture stands ready to assist you with your long-running tasks, but also includes a small number of performance traps.
-
对于长时间运行的 CPU 密集型任务,请使用 工作线程,考虑将它们移动到 BrowserWindow,或者(作为最后的手段)生成一个专用进程。
¥For long running CPU-heavy tasks, make use of worker threads, consider moving them to the BrowserWindow, or (as a last resort) spawn a dedicated process.
-
尽量避免使用同步 IPC 和
@electron/remote
模块。虽然存在合法的用例,但很容易在不知不觉中阻塞 UI 线程。¥Avoid using the synchronous IPC and the
@electron/remote
module as much as possible. While there are legitimate use cases, it is far too easy to unknowingly block the UI thread. -
避免在主进程中使用阻塞 I/O 操作。简而言之,每当核心 Node.js 模块(如
fs
或child_process
)提供同步或异步版本时,你应该更喜欢异步和非阻塞变体。¥Avoid using blocking I/O operations in the main process. In short, whenever core Node.js modules (like
fs
orchild_process
) offer a synchronous or an asynchronous version, you should prefer the asynchronous and non-blocking variant.
4. 阻塞渲染进程
¥ Blocking the renderer process
由于 Electron 附带了当前版本的 Chrome,因此你可以利用 Web 平台提供的最新、最强大的功能来推迟或卸载繁重的操作,从而保持应用的流畅性和响应能力。
¥Since Electron ships with a current version of Chrome, you can make use of the latest and greatest features the Web Platform offers to defer or offload heavy operations in a way that keeps your app smooth and responsive.
为什么?
¥Why?
你的应用可能有大量 JavaScript 要在渲染器进程中运行。诀窍是尽可能快地执行操作,而不占用保持滚动平滑、响应用户输入或 60fps 动画所需的资源。
¥Your app probably has a lot of JavaScript to run in the renderer process. The trick is to execute operations as quickly as possible without taking away resources needed to keep scrolling smooth, respond to user input, or animations at 60fps.
如果用户有时抱怨你的应用,那么在渲染器代码中编排操作流程尤其有用 "stuttering"。
¥Orchestrating the flow of operations in your renderer's code is particularly useful if users complain about your app sometimes "stuttering".
如何?
¥How?
一般来说,为现代浏览器构建高性能 Web 应用的所有建议也适用于 Electron 的渲染器。目前,你可以使用的两个主要工具是用于小型操作的 requestIdleCallback()
和用于长时间运行操作的 Web Workers
。
¥Generally speaking, all advice for building performant web apps for modern
browsers apply to Electron's renderers, too. The two primary tools at your
disposal are currently requestIdleCallback()
for small operations and
Web Workers
for long-running operations.
requestIdleCallback()
允许开发者在进程进入空闲期时立即排队执行函数。它使你能够执行低优先级或后台工作,而不会影响用户体验。有关如何使用它的更多信息,查看 MDN 上的文档。
¥requestIdleCallback()
allows developers to queue up a function to be
executed as soon as the process is entering an idle period. It enables you to
perform low-priority or background work without impacting the user experience.
For more information about how to use it,
check out its documentation on MDN.
Web Workers 是在单独线程上运行代码的强大工具。有一些注意事项需要考虑 - 请参阅 Electron 的 多线程文档 和 Web Worker 的 MDN 文档。它们是任何长时间需要大量 CPU 处理能力的操作的理想解决方案。
¥Web Workers are a powerful tool to run code on a separate thread. There are some caveats to consider – consult Electron's multithreading documentation and the MDN documentation for Web Workers. They're an ideal solution for any operation that requires a lot of CPU power for an extended period of time.
5. 不必要的填充
¥ Unnecessary polyfills
Electron 的一大好处是你确切地知道哪个引擎将解析你的 JavaScript、HTML 和 CSS。如果你要重新利用为整个 Web 编写的代码,请确保不要使用 Electron 中包含的 Polyfill 功能。
¥One of Electron's great benefits is that you know exactly which engine will parse your JavaScript, HTML, and CSS. If you're re-purposing code that was written for the web at large, make sure to not polyfill features included in Electron.
为什么?
¥Why?
在为当今的 Internet 构建 Web 应用时,最古老的环境决定了你可以使用和不能使用哪些功能。尽管 Electron 支持性能良好的 CSS 过滤器和动画,但较旧的浏览器可能不支持。在可以使用 WebGL 的地方,你的开发者可能选择了更需要资源的解决方案来支持旧手机。
¥When building a web application for today's Internet, the oldest environments dictate what features you can and cannot use. Even though Electron supports well-performing CSS filters and animations, an older browser might not. Where you could use WebGL, your developers may have chosen a more resource-hungry solution to support older phones.
当涉及到 JavaScript 时,你可能已经包含了用于 DOM 选择器的 jQuery 等工具包库,或者用于支持 async/await
的 regenerator-runtime
等填充工具。
¥When it comes to JavaScript, you may have included toolkit libraries like
jQuery for DOM selectors or polyfills like the regenerator-runtime
to support
async/await
.
基于 JavaScript 的 polyfill 很少能比 Electron 中同等的原生功能更快。不要通过发布你自己版本的标准 Web 平台功能来减慢你的 Electron 应用的速度。
¥It is rare for a JavaScript-based polyfill to be faster than the equivalent native feature in Electron. Do not slow down your Electron app by shipping your own version of standard web platform features.
如何?
¥How?
假设当前版本的 Electron 中的 polyfill 是不必要的。如果你有疑问,请检查 caniuse.com 并检查 Electron 版本中使用的 Chromium 版本 是否支持你想要的功能。
¥Operate under the assumption that polyfills in current versions of Electron are unnecessary. If you have doubts, check caniuse.com and check if the version of Chromium used in your Electron version supports the feature you desire.
此外,请仔细检查你使用的库。它们真的有必要吗?例如,jQuery
非常成功,其许多功能现已成为 可用的标准 JavaScript 功能集 的一部分。
¥In addition, carefully examine the libraries you use. Are they really necessary?
jQuery
, for example, was such a success that many of its features are now part
of the standard JavaScript feature set available.
如果你使用的是 TypeScript 等转译器/编译器,请检查其配置并确保你的目标是 Electron 支持的最新 ECMAScript 版本。
¥If you're using a transpiler/compiler like TypeScript, examine its configuration and ensure that you're targeting the latest ECMAScript version supported by Electron.
6. 不必要或阻塞的网络请求
¥ Unnecessary or blocking network requests
如果可以轻松地将这些资源与你的应用打包在一起,请避免从互联网上获取很少变化的资源。
¥Avoid fetching rarely changing resources from the internet if they could easily be bundled with your application.
为什么?
¥Why?
许多 Electron 用户从完全基于 Web 的应用开始,并将其转变为桌面应用。作为网络开发者,我们习惯于从各种内容交付网络加载资源。既然你正在发布正确的桌面应用,请尽可能尝试 "剪断脐带",并避免让你的用户等待永远不会更改且可以轻松包含在你的应用中的资源。
¥Many users of Electron start with an entirely web-based app that they're turning into a desktop application. As web developers, we are used to loading resources from a variety of content delivery networks. Now that you are shipping a proper desktop application, attempt to "cut the cord" where possible and avoid letting your users wait for resources that never change and could easily be included in your app.
一个典型的例子是谷歌字体。许多开发者都利用 Google 令人印象深刻的免费字体集合,这些字体带有内容交付网络。音调很简单:添加几行 CSS,Google 将处理其余的事情。
¥A typical example is Google Fonts. Many developers make use of Google's impressive collection of free fonts, which comes with a content delivery network. The pitch is straightforward: Include a few lines of CSS and Google will take care of the rest.
构建 Electron 应用时,如果你下载字体并将其包含在应用包中,你的用户会得到更好的服务。
¥When building an Electron app, your users are better served if you download the fonts and include them in your app's bundle.
如何?
¥How?
在理想的情况下,你的应用根本不需要网络来运行。为此,你必须了解你的应用正在下载哪些资源 - 以及这些资源有多大。
¥In an ideal world, your application wouldn't need the network to operate at all. To get there, you must understand what resources your app is downloading
- and how large those resources are.
为此,请打开开发者工具。导航到 Network
选项卡并选中 Disable cache
选项。然后,重新加载渲染器。除非你的应用禁止此类重新加载,否则你通常可以通过在焦点位于开发者工具的情况下点击 Cmd + R
或 Ctrl + R
来触发重新加载。
¥To do so, open up the developer tools. Navigate to the Network
tab and check
the Disable cache
option. Then, reload your renderer. Unless your app
prohibits such reloads, you can usually trigger a reload by hitting Cmd + R
or Ctrl + R
with the developer tools in focus.
这些工具现在将仔细记录所有网络请求。在第一遍中,盘点正在下载的所有资源,首先关注较大的文件。其中是否有任何不会更改且可以包含在你的打包包中的图片、字体或媒体文件?如果是这样,请包括它们。
¥The tools will now meticulously record all network requests. In a first pass, take stock of all the resources being downloaded, focusing on the larger files first. Are any of them images, fonts, or media files that don't change and could be included with your bundle? If so, include them.
下一步,启用 Network Throttling
。找到当前显示 Online
的下拉菜单,然后选择较慢的速度,例如 Fast 3G
。重新加载渲染器并查看你的应用是否有不必要等待的资源。在许多情况下,应用将等待网络请求完成,尽管实际上并不需要所涉及的资源。
¥As a next step, enable Network Throttling
. Find the drop-down that currently
reads Online
and select a slower speed such as Fast 3G
. Reload your
renderer and see if there are any resources that your app is unnecessarily
waiting for. In many cases, an app will wait for a network request to complete
despite not actually needing the involved resource.
作为提示,从 Internet 加载你可能想要更改的资源而不发送应用更新是一个强大的策略。要对资源加载方式进行高级控制,请考虑投资 服务工作进程。
¥As a tip, loading resources from the Internet that you might want to change without shipping an application update is a powerful strategy. For advanced control over how resources are being loaded, consider investing in Service Workers.
7. 打包你的代码
¥ Bundle your code
正如“加载和运行代码太快”中已经指出的,调用 require()
是一个昂贵的操作。如果可以的话,请将应用的代码打包到一个文件中。
¥As already pointed out in
"Loading and running code too soon",
calling require()
is an expensive operation. If you are able to do so,
bundle your application's code into a single file.
为什么?
¥Why?
现代 JavaScript 开发通常涉及许多文件和模块。虽然这非常适合使用 Electron 进行开发,但我们强烈建议你将所有代码打包到一个文件中,以确保调用 require()
所包含的开销仅在应用加载时支付一次。
¥Modern JavaScript development usually involves many files and modules. While
that's perfectly fine for developing with Electron, we heavily recommend that
you bundle all your code into one single file to ensure that the overhead
included in calling require()
is only paid once when your application loads.
如何?
¥How?
市面上有很多 JavaScript 打包器,我们知道最好不要通过推荐一种工具而不是另一种工具来激怒社区。然而,我们建议你使用能够处理 Electron 独特环境的打包器,该环境需要处理 Node.js 和浏览器环境。
¥There are numerous JavaScript bundlers out there and we know better than to anger the community by recommending one tool over another. We do however recommend that you use a bundler that is able to handle Electron's unique environment that needs to handle both Node.js and browser environments.
截至撰写本文时,流行的选择包括 Webpack、Parcel 和 rollup.js。
¥As of writing this article, the popular choices include Webpack, Parcel, and rollup.js.
8. 当你不需要默认菜单时,请调用 Menu.setApplicationMenu(null)
¥ Call Menu.setApplicationMenu(null)
when you do not need a default menu
Electron 将在启动时设置一个默认菜单,其中包含一些标准条目。但你的应用可能出于某些原因想要更改这一点,这将有利于启动性能。
¥Electron will set a default menu on startup with some standard entries. But there are reasons your application might want to change that and it will benefit startup performance.
为什么?
¥Why?
如果你构建自己的菜单或使用没有原生菜单的无框架窗口,你应该尽早告诉 Electron 不要设置默认菜单。
¥If you build your own menu or use a frameless window without native menu, you should tell Electron early enough to not setup the default menu.
如何?
¥How?
在 app.on("ready")
之前调用 Menu.setApplicationMenu(null)
。这将阻止 Electron 设置默认菜单。另请参阅 https://github.com/electron/electron/issues/35512 的相关讨论。
¥Call Menu.setApplicationMenu(null)
before app.on("ready")
. This will prevent Electron from setting a default menu. See also https://github.com/electron/electron/issues/35512 for a related discussion.