Skip to main content

性能

🌐 Performance

开发者经常询问如何优化 Electron 应用性能的策略。软件工程师、用户以及框架开发者并不总是对“性能”这一概念有统一的定义。本文档概述了一些 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 应用的资源也适用,但需要注意的是,“性能”这一术语对于 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 Tracing 工具。

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

  1. 不小心包含模块
  2. 加载和运行代码过早
  3. 阻塞主进程
  4. 阻塞渲染进程
  5. 不必要的 polyfill
  6. 不必要或阻塞的网络请求
  7. 打包你的代码
  8. 当你不需要默认菜单时调用 Menu.setApplicationMenu(null)

1. 粗心地包含模块

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

该模块通过尝试访问多个知名端点来检测你的网络连接性。对于这些端点的列表,它依赖于另一个模块,该模块还包含一个知名端口列表。这个依赖本身又依赖于一个包含端口信息的模块,该信息以一个包含超过 100,000 行内容的 JSON 文件的形式存在。每当该模块被加载(通常在 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 服务器需要获取所有端口的信息,那么在服务器启动时将所有所需信息加载到内存中实际上可能“性能更好”,因为这样可以更快地响应请求。此示例中讨论的模块并不是“不好的”模块。然而,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:

  1. 包含的依赖的大小
  2. 加载 (require()) 所需的资源
  3. 执行你感兴趣的操作所需的资源

可以通过命令行使用单个命令生成加载模块的 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 开发者工具进行分析,分别使用 PerformanceMemory 选项卡。

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

Performance CPU Profile

Performance Heap Memory Profile

在这个例子中,在作者的机器上,我们看到加载 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. 过早加载和运行代码

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

parser.js
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?

parser.js
// "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. 阻塞主进程

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

  1. 对于长期运行的高 CPU 任务,使用 工作线程,考虑将它们移动到 BrowserWindow,或者(作为最后手段)启动一个专用进程。
  2. 尽量避免使用同步 IPC 和 @electron/remote 模块。虽然有一些合理的使用场景,但很容易在不知不觉中阻塞 UI 线程。
  3. 避免在主进程中使用阻塞 I/O 操作。简而言之,每当核心 Node.js 模块(如 fschild_process)提供同步或异步版本时,你应该优先使用异步和非阻塞的版本。

4. 阻塞渲染器进程

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

在渲染器的代码中协调操作进程尤其有用,如果用户抱怨你的应用有时会“卡顿”。

🌐 Orchestrating the flow of operations in your renderer's code is particularly useful if users complain about your app sometimes "stuttering".

如何?

🌐 How?

一般来说,为现代浏览器构建高性能网页应用的所有建议同样适用于 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 的 多线程文档MDN Web Workers 文档。对于任何需要长时间大量使用 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. 不必要的 polyfill

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

在为当今的互联网构建 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 时,你可能会引入像 jQuery 这样的工具库来进行 DOM 选择,或者像 regenerator-runtime 这样的垫片来支持 async/await

🌐 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 中等效的原生功能更快的情况很少见。不要通过使用你自己实现的标准网页平台功能版本来减慢 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. 不必要或阻塞的网络请求

🌐 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 用户一开始都是使用完全基于网页的应用,然后将其转变为桌面应用。作为网页开发者,我们习惯于从各种内容分发网络加载资源。现在你要发布一个真正的桌面应用时,尽量“切断连接”,避免让用户等待那些从未改变、且可以轻松包含在应用中的资源。

🌐 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 字体。许多开发者使用 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 + RCtrl + 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.

作为一个提示,从互联网上加载资源而不需要通过应用更新来更改,这是一个强大的策略。为了对资源加载方式进行高级控制,可以考虑投资服务工作进程

🌐 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. 打包你的代码

🌐 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封装rollup.js

🌐 As of writing this article, the popular choices include Webpack, Parcel, and rollup.js.

8. 当你不需要默认菜单时,调用 Menu.setApplicationMenu(null)

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