Skip to main content

快速开始

¥Quick Start

本指南将引导你完成在 Electron 中创建准系统 Hello World 应用的过程,类似于 electron/electron-quick-start

¥This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.

在本教程结束时,你的应用将打开一个浏览器窗口,其中显示一个网页,其中包含有关正在运行的 Chromium、Node.js 和 Electron 版本的信息。

¥By the end of this tutorial, your app will open a browser window that displays a web page with information about which Chromium, Node.js, and Electron versions are running.

先决条件

¥Prerequisites

要使用 Electron,你需要安装 Node.js。我们建议你使用最新的 LTS 版本。

¥To use Electron, you need to install Node.js. We recommend that you use the latest LTS version available.

请使用适合你平台的预构建安装程序来安装 Node.js。否则,你可能会遇到不同开发工具的不兼容问题。

¥Please install Node.js using pre-built installers for your platform. You may encounter incompatibility issues with different development tools otherwise.

要检查 Node.js 是否已正确安装,请在终端客户端中键入以下命令:

¥To check that Node.js was installed correctly, type the following commands in your terminal client:

node -v
npm -v

这些命令应相应地打印 Node.js 和 npm 的版本。

¥The commands should print the versions of Node.js and npm accordingly.

注意:由于 Electron 将 Node.js 嵌入到其二进制文件中,因此运行代码的 Node.js 版本与系统上运行的版本无关。

¥Note: Since Electron embeds Node.js into its binary, the version of Node.js running your code is unrelated to the version running on your system.

创建你的应用

¥Create your application

搭建项目脚手架

¥Scaffold the project

Electron 应用遵循与其他 Node.js 项目相同的总体结构。首先创建一个文件夹并初始化一个 npm 包。

¥Electron apps follow the same general structure as other Node.js projects. Start by creating a folder and initializing an npm package.

mkdir my-electron-app && cd my-electron-app
npm init

交互式 init 命令将提示你在配置中设置一些字段。出于本教程的目的,需要遵循一些规则:

¥The interactive init command will prompt you to set some fields in your config. There are a few rules to follow for the purposes of this tutorial:

  • entry point 应该是 main.js

    ¥entry point should be main.js.

  • authordescription 可以是任何值,但对于 应用打包 是必需的。

    ¥author and description can be any value, but are necessary for app packaging.

你的 package.json 文件应如下所示:

¥Your package.json file should look something like this:

{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT"
}

然后,将 electron 包安装到应用的 devDependencies 中。

¥Then, install the electron package into your app's devDependencies.

npm install --save-dev electron

注意:如果你在安装 Electron 时遇到任何问题,请参阅 高级安装 指南。

¥Note: If you're encountering any issues with installing Electron, please refer to the Advanced Installation guide.

最后,你希望能够执行 Electron。在 package.json 配置的 scripts 字段中,添加 start 命令,如下所示:

¥Finally, you want to be able to execute Electron. In the scripts field of your package.json config, add a start command like so:

{
"scripts": {
"start": "electron ."
}
}

start 命令将允许你在开发模式下打开应用。

¥This start command will let you open your app in development mode.

npm start

注意:该脚本告诉 Electron 在项目的根文件夹上运行。在此阶段,你的应用将立即抛出错误,告诉你它找不到要运行的应用。

¥Note: This script tells Electron to run on your project's root folder. At this stage, your app will immediately throw an error telling you that it cannot find an app to run.

运行主进程

¥Run the main process

任何 Electron 应用的入口点都是其 main 脚本。该脚本控制主进程,该进程在完整的 Node.js 环境中运行,负责控制应用的生命周期、显示原生界面、执行特权操作以及管理渲染器进程(稍后会详细介绍)。

¥The entry point of any Electron application is its main script. This script controls the main process, which runs in a full Node.js environment and is responsible for controlling your app's lifecycle, displaying native interfaces, performing privileged operations, and managing renderer processes (more on that later).

在执行过程中,Electron 将在应用的 package.json 配置的 main 字段中查找此脚本,你应该在 应用脚手架 步骤中配置该脚本。

¥During execution, Electron will look for this script in the main field of the app's package.json config, which you should have configured during the app scaffolding step.

要初始化 main 脚本,请在项目的根文件夹中创建一个名为 main.js 的空文件。

¥To initialize the main script, create an empty file named main.js in the root folder of your project.

注意:如果此时再次运行 start 脚本,你的应用将不再抛出任何错误!然而,它还不会做任何事情,因为我们还没有向 main.js 添加任何代码。

¥Note: If you run the start script again at this point, your app will no longer throw any errors! However, it won't do anything yet because we haven't added any code into main.js.

创建网页

¥Create a web page

在为应用创建窗口之前,我们需要创建将加载到其中的内容。在 Electron 中,每个窗口都显示可以从本地 HTML 文件或远程 URL 加载的 Web 内容。

¥Before we can create a window for our application, we need to create the content that will be loaded into it. In Electron, each window displays web contents that can be loaded from either a local HTML file or a remote URL.

在本教程中,你将执行前者。在项目的根文件夹中创建 index.html 文件:

¥For this tutorial, you will be doing the former. Create an index.html file in the root folder of your project:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- https://web.nodejs.cn/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
</body>
</html>

注意:查看此 HTML 文档,你可以发现正文中缺少版本号。稍后我们将使用 JavaScript 手动插入它们。

¥Note: Looking at this HTML document, you can observe that the version numbers are missing from the body text. We'll manually insert them later using JavaScript.

在浏览器窗口中打开网页

¥Opening your web page in a browser window

现在你已经有了一个网页,将其加载到应用窗口中。为此,你需要两个 Electron 模块:

¥Now that you have a web page, load it into an application window. To do so, you'll need two Electron modules:

  • app 模块,控制应用的事件生命周期。

    ¥The app module, which controls your application's event lifecycle.

  • BrowserWindow 模块,用于创建和管理应用窗口。

    ¥The BrowserWindow module, which creates and manages application windows.

由于主进程运行 Node.js,因此你可以将它们作为 CommonJS 模块导入到 main.js 文件的顶部:

¥Because the main process runs Node.js, you can import these as CommonJS modules at the top of your main.js file:

const { app, BrowserWindow } = require('electron')

然后,添加一个 createWindow() 函数,将 index.html 加载到新的 BrowserWindow 实例中。

¥Then, add a createWindow() function that loads index.html into a new BrowserWindow instance.

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile('index.html')
}

接下来,调用此 createWindow() 函数来打开窗口。

¥Next, call this createWindow() function to open your window.

在 Electron 中,浏览器窗口只能在 app 模块的 ready 事件被触发后创建。你可以使用 app.whenReady() API 等待此事件。在 whenReady() 解决其 Promise 后调用 createWindow()

¥In Electron, browser windows can only be created after the app module's ready event is fired. You can wait for this event by using the app.whenReady() API. Call createWindow() after whenReady() resolves its Promise.

app.whenReady().then(() => {
createWindow()
})

注意:此时,你的 Electron 应用应该成功打开一个显示你的网页的窗口!

¥Note: At this point, your Electron application should successfully open a window that displays your web page!

管理窗口的生命周期

¥Manage your window's lifecycle

尽管你现在可以打开浏览器窗口,但你将需要一些额外的样板代码,以使其感觉对每个平台更加原生。应用窗口在每个操作系统上的行为都不同,Electron 让开发者有责任在他们的应用中实现这些约定。

¥Although you can now open a browser window, you'll need some additional boilerplate code to make it feel more native to each platform. Application windows behave differently on each OS, and Electron puts the responsibility on developers to implement these conventions in their app.

一般来说,你可以使用 process 全局的 platform 属性来运行专门针对某些操作系统的代码。

¥In general, you can use the process global's platform attribute to run code specifically for certain operating systems.

所有窗口关闭后退出应用(Windows 和 Linux)

¥Quit the app when all windows are closed (Windows & Linux)

在 Windows 和 Linux 上,退出所有窗口通常会完全退出应用。

¥On Windows and Linux, exiting all windows generally quits an application entirely.

要实现此目的,请监听 app 模块的 'window-all-closed' 事件,如果用户不在 macOS (darwin) 上,则调用 app.quit()

¥To implement this, listen for the app module's 'window-all-closed' event, and call app.quit() if the user is not on macOS (darwin).

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

如果没有打开窗口,则打开一个窗口 (macOS)

¥Open a window if none are open (macOS)

Linux 和 Windows 应用在没有打开任何窗口时退出,而 macOS 应用通常即使没有打开任何窗口也会继续运行,并且在没有可用窗口时激活应用应该打开一个新窗口。

¥Whereas Linux and Windows apps quit when they have no windows open, macOS apps generally continue running even without any windows open, and activating the app when no windows are available should open a new one.

要实现此功能,请监听 app 模块的 activate 事件,如果没有打开浏览器窗口,则调用现有的 createWindow() 方法。

¥To implement this feature, listen for the app module's activate event, and call your existing createWindow() method if no browser windows are open.

由于无法在 ready 事件之前创建窗口,因此你应该仅在应用初始化后监听 activate 事件。通过从现有 whenReady() 回调中附加事件监听器来执行此操作。

¥Because windows cannot be created before the ready event, you should only listen for activate events after your app is initialized. Do this by attaching your event listener from within your existing whenReady() callback.

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

注意:此时,你的窗口控件应该功能齐全!

¥Note: At this point, your window controls should be fully functional!

使用预加载脚本从渲染器访问 Node.js

¥Access Node.js from the renderer with a preload script

现在,最后要做的是将 Electron 的版本号及其依赖打印到你的网页上。

¥Now, the last thing to do is print out the version numbers for Electron and its dependencies onto your web page.

在主进程中通过 Node 的全局 process 对象访问此信息很简单。但是,你不能只从主进程编辑 DOM,因为它无法访问渲染器的 document 上下文。他们处于完全不同的进程!

¥Accessing this information is trivial to do in the main process through Node's global process object. However, you can't just edit the DOM from the main process because it has no access to the renderer's document context. They're in entirely different processes!

注意:如果你需要更深入地了解 Electron 进程,请参阅 进程模型 文档。

¥Note: If you need a more in-depth look at Electron processes, see the Process Model document.

这时将预加载脚本附加到渲染器就派上用场了。预加载脚本在加载渲染器进程之前运行,并且可以访问渲染器全局变量(例如 windowdocument)和 Node.js 环境。

¥This is where attaching a preload script to your renderer comes in handy. A preload script runs before the renderer process is loaded, and has access to both renderer globals (e.g. window and document) and a Node.js environment.

创建一个名为 preload.js 的新脚本,如下所示:

¥Create a new script named preload.js as such:

window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}

for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})

上面的代码访问 Node.js process.versions 对象并运行基本的 replaceText 辅助函数以将版本号插入到 HTML 文档中。

¥The above code accesses the Node.js process.versions object and runs a basic replaceText helper function to insert the version numbers into the HTML document.

要将此脚本附加到渲染器进程,请将预加载脚本的路径传递给现有 BrowserWindow 构造函数中的 webPreferences.preload 选项。

¥To attach this script to your renderer process, pass in the path to your preload script to the webPreferences.preload option in your existing BrowserWindow constructor.

const { app, BrowserWindow } = require('electron')
// include the Node.js 'path' module at the top of your file
const path = require('node:path')

// modify your existing createWindow() function
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')
}
// ...

这里使用了两个 Node.js 概念:

¥There are two Node.js concepts that are used here:

  • __dirname 字符串指向当前正在执行的脚本的路径(在本例中为项目的根文件夹)。

    ¥The __dirname string points to the path of the currently executing script (in this case, your project's root folder).

  • path.join API 将多个路径段连接在一起,创建一个适用于所有平台的组合路径字符串。

    ¥The path.join API joins multiple path segments together, creating a combined path string that works across all platforms.

我们使用相对于当前正在执行的 JavaScript 文件的路径,以便你的相对路径可以在开发模式和打包模式下工作。

¥We use a path relative to the currently executing JavaScript file so that your relative path will work in both development and packaged mode.

奖金:为你的网页内容添加功能

¥Bonus: Add functionality to your web contents

此时,你可能想知道如何向应用添加更多功能。

¥At this point, you might be wondering how to add more functionality to your application.

对于与 Web 内容的任何交互,你需要将脚本添加到渲染器进程中。由于渲染器在正常的 Web 环境中运行,因此你可以在 index.html 文件的结束 </body> 标记之前添加 <script> 标记,以包含你想要的任意脚本:

¥For any interactions with your web contents, you want to add scripts to your renderer process. Because the renderer runs in a normal web environment, you can add a <script> tag right before your index.html file's closing </body> tag to include any arbitrary scripts you want:

<script src="./renderer.js"></script>

然后,renderer.js 中包含的代码可以使用与典型前端开发相同的 JavaScript API 和工具,例如使用 webpack 来打包和缩小代码,或使用 React 来管理用户界面。

¥The code contained in renderer.js can then use the same JavaScript APIs and tooling you use for typical front-end development, such as using webpack to bundle and minify your code or React to manage your user interfaces.

回顾

¥Recap

执行上述步骤后,你应该拥有一个功能齐全的 Electron 应用,如下所示:

¥After following the above steps, you should have a fully functional Electron application that looks like this:

Simplest Electron app

完整代码如下:

¥The full code is available below:

// main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('node:path')

const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

// and load the index.html of the app.
mainWindow.loadFile('index.html')

// Open the DevTools.
// mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// preload.js

// All the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}

for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- https://web.nodejs.cn/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.

<!-- You can also require other files to run in this process -->
<script src="./renderer.js"></script>
</body>
</html>
const { app, BrowserWindow } = require('electron/main')
const path = require('node:path')

function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

总结一下我们所做的所有步骤:

¥To summarize all the steps we've done:

  • 我们引导了一个 Node.js 应用并添加了 Electron 作为依赖。

    ¥We bootstrapped a Node.js application and added Electron as a dependency.

  • 我们创建了一个运行主进程的 main.js 脚本,该进程控制我们的应用并在 Node.js 环境中运行。在此脚本中,我们使用 Electron 的 appBrowserWindow 模块创建一个浏览器窗口,该窗口在单独的进程(渲染器)中显示 Web 内容。

    ¥We created a main.js script that runs our main process, which controls our app and runs in a Node.js environment. In this script, we used Electron's app and BrowserWindow modules to create a browser window that displays web content in a separate process (the renderer).

  • 为了访问渲染器中的某些 Node.js 功能,我们将预加载脚本附加到 BrowserWindow 构造函数中。

    ¥In order to access certain Node.js functionality in the renderer, we attached a preload script to our BrowserWindow constructor.

打包并分发你的应用

¥Package and distribute your application

分发新创建的应用的最快方法是使用 Electron Forge

¥The fastest way to distribute your newly created app is using Electron Forge.

信息

要为 Linux 构建 RPM 软件包,你需要 安装所需的系统依赖

¥To build an RPM package for Linux, you will need to install its required system dependencies.

  1. package.json 文件中添加描述,否则 rpmbuild 将失败。空白描述无效。

    ¥Add a description to your package.json file, otherwise rpmbuild will fail. Blank description are not valid.

  2. 添加 Electron Forge 作为应用的开发依赖,并使用其 import 命令来设置 Forge 的脚手架:

    ¥Add Electron Forge as a development dependency of your app, and use its import command to set up Forge's scaffolding:

    npm install --save-dev @electron-forge/cli
    npx electron-forge import

    ✔ Checking your system
    ✔ Initializing Git Repository
    ✔ Writing modified package.json file
    ✔ Installing dependencies
    ✔ Writing modified package.json file
    ✔ Fixing .gitignore

    We have ATTEMPTED to convert your app to be in a format that electron-forge understands.

    Thanks for using "electron-forge"!!!
  3. 使用 Forge 的 make 命令创建可分发文件:

    ¥Create a distributable using Forge's make command:

    npm run make

    > my-electron-app@1.0.0 make /my-electron-app
    > electron-forge make

    ✔ Checking your system
    ✔ Resolving Forge Config
    We need to package your application before we can make it
    ✔ Preparing to Package Application for arch: x64
    ✔ Preparing native dependencies
    ✔ Packaging Application
    Making for the following targets: zip
    ✔ Making for target: zip - On platform: darwin - For arch: x64

    Electron Forge 创建 out 文件夹,你的包将位于其中:

    ¥Electron Forge creates the out folder where your package will be located:

    // Example for macOS
    out/
    ├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
    ├── ...
    └── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app