Building your First App构建您的第一个应用程序
This is part 2 of the Electron tutorial.这是Electron教程的第2部分。
Learning goals学习目标
In this part of the tutorial, you will learn how to set up your Electron project and write a minimal starter application. 在本教程的这一部分中,您将学习如何设置您的Electron项目并编写一个最小的入门应用程序。By the end of this section, you should be able to run a working Electron app in development mode from your terminal.在本节结束时,您应该能够从终端以开发模式运行一个工作的Electron应用程序。
Setting up your project设置您的项目
If you are on a Windows machine, please do not use Windows Subsystem for Linux (WSL) when following this tutorial as you will run into issues when trying to execute the application.如果您在Windows计算机上,请不要在遵循本教程时使用Windows Linux子系统(WSL),因为您在尝试执行应用程序时会遇到问题。
Initializing your npm project初始化您的npm项目
Electron apps are scaffolded using npm, with the package.json file as an entry point. Electron应用程序使用npm构建,以package.json
文件作为入口点。Start by creating a folder and initializing an npm package within it with 首先创建一个文件夹,并使用npm init
.npm init
初始化其中的npm包。
- npm
- Yarn
mkdir my-electron-app && cd my-electron-app
npm init
mkdir my-electron-app && cd my-electron-app
yarn init
This command will prompt you to configure some fields in your package.json. 此命令将提示您在package.json
中配置一些字段。There are a few rules to follow for the purposes of this tutorial:在本教程中,有一些规则需要遵循:
entry point should be入口点应该是main.js
(you will be creating that file soon).main.js
(您将很快创建该文件)。author, license, and description can be any value, but will be necessary for packaging later on.作者、许可证和描述可以是任何值,但在以后的打包中是必需的。
Then, install Electron into your app's devDependencies, which is the list of external development-only package dependencies not required in production.然后,将Electron安装到应用程序的devDependencies
中,这是生产中不需要的外部开发包依赖项列表。
This may seem counter-intuitive since your production code is running Electron APIs. 由于您的生产代码运行的是ElectronAPI,因此这看起来可能违反直觉。However, packaged apps will come bundled with the Electron binary, eliminating the need to specify it as a production dependency.然而,打包应用程序将与Electron二进制文件捆绑在一起,无需将其指定为生产依赖项。
- npm
- Yarn
npm install electron --save-dev
yarn add electron --dev
Your package.json file should look something like this after initializing your package and installing Electron. 初始化包并安装Electron后,您的package.json
文件应该如下所示。You should also now have a 现在,您还应该有一个包含Electron可执行文件的node_modules
folder containing the Electron executable, as well as a package-lock.json
lockfile that specifies the exact dependency versions to install.node_modules
文件夹,以及一个指定要安装的确切依赖版本的package-lock.json
锁文件。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "19.0.0"
}
}
If installing Electron directly fails, please refer to our Advanced Installation documentation for instructions on download mirrors, proxies, and troubleshooting steps.如果直接安装Electron失败,请参阅高级安装文档,了解有关下载镜像、代理和故障排除步骤的说明。
Adding a 添加.gitignore
The .gitignore file specifies which files and directories to avoid tracking with Git. .gitignore文件指定要避免使用Git跟踪的文件和目录。You should place a copy of GitHub's Node.js gitignore template into your project's root folder to avoid committing your project's 您应该将GitHub的Node.js gitignore模板的副本放在项目的根文件夹中,以避免提交项目的node_modules
folder.node_modules
文件夹。
Running an Electron app运行Electron应用程序
Read Electron's process model documentation to better understand how Electron's multiple processes work together.阅读Electron的过程模型文档,以更好地了解Electron多个过程如何协同工作。
The main script you defined in package.json is the entry point of any Electron application. 在package.json
中定义的主脚本是任何Electron应用程序的入口点。This script controls the main process, which runs in a 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).该脚本控制主进程,主进程在Node.js环境中运行,负责控制应用程序的生命周期、显示本机接口、执行特权操作和管理渲染器进程(稍后将详细介绍)。
Before creating your first Electron app, you will first use a trivial script to ensure your main process entry point is configured correctly. 在创建第一个Electron应用程序之前,您将首先使用一个简单的脚本来确保主流程入口点配置正确。Create a 使用一行代码在项目的根文件夹中创建main.js
file in the root folder of your project with a single line of code:main.js
文件:
console.log(`Hello from Electron 👋`)
Because Electron's main process is a Node.js runtime, you can execute arbitrary Node.js code with the 因为Electron的主进程是Node.js运行时,所以您可以使用electron
command (you can even use it as a REPL). electron
命令执行任意Node.js代码(甚至可以将其用作REPL)。To execute this script, add 要执行此脚本,请在electron .
to the start
command in the scripts field of your package.json. package.json
的脚本字段中的start
命令中添加electron
。This command will tell the Electron executable to look for the main script in the current directory and run it in dev mode.该命令将告诉Electron可执行文件在当前目录中查找主脚本,并在开发模式下运行。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^19.0.0"
}
}
- npm
- Yarn
npm run start
yarn run start
Your terminal should print out 终端将打印出Hello from Electron 👋
. Hello from Electron 👋
。Congratulations, you have executed your first line of code in Electron! 祝贺您,您已经在Electron中执行了第一行代码!Next, you will learn how to create user interfaces with HTML and load that into a native window.接下来,您将学习如何使用HTML创建用户界面,并将其加载到本机窗口中。
Loading a web page into a BrowserWindow将网页加载到BrowserWindow
In Electron, each window displays a web page that can be loaded either from a local HTML file or a remote web address. 在Electron中,每个窗口都显示一个网页,可以从本地HTML文件或远程web地址加载。For this example, you will be loading in a local file. 对于本例,您将在本地文件中加载。Start by creating a barebones web page in an 首先,在项目根文件夹中的index.html
file in the root folder of your project:index.html
文件中创建一个基本网页:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- http://mdn.asprain.cn/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>
Now that you have a web page, you can load it into an Electron BrowserWindow. 现在您有了一个网页,您可以将其加载到ElectronBrowserWindow中。Replace the contents your 用以下代码替换main.js
file with the following code. main.js
文件的内容。We will explain each highlighted block separately.我们将分别解释每个突出显示的块。
const { app, BrowserWindow } = require('electron')const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, }) win.loadFile('index.html')}app.whenReady().then(() => { createWindow()})
Importing modules导入模块
const { app, BrowserWindow } = require('electron')
In the first line, we are importing two Electron modules with CommonJS module syntax:在第一行中,我们使用CommonJS模块语法导入两个Electron模块:
- app
, which controls your application's event lifecycle.,它控制应用程序的事件生命周期。 - BrowserWindow
, which creates and manages app windows.,创建和管理应用程序窗口。
You might have noticed the capitalization difference between the app and BrowserWindow modules. 您可能已经注意到app和BrowserWindow模块之间的大小写差异。Electron follows typical JavaScript conventions here, where PascalCase modules are instantiable class constructors (e.g. BrowserWindow, Tray, Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRenderer, webContents).Electron在这里遵循典型的JavaScript约定,其中PascalCase模块是可实例化的类构造函数(例如BrowserWindow、Tray、Notification),而camelCase模块不是可实例化的(例如app、ipcRenderer、webContents)。
ECMAScript modulesECMAScript模块 (i.e. using (即,使用import
to load a module) are currently not directly supported in Electron. import
加载模块)目前在Electron中不直接支持。You can find more information about the state of ESM in Electron in electron/electron#21457.您可以在electron/electron#21457中找到有关Electron中ESM状态的更多信息。
Writing a reusable function to instantiate windows编写可重用函数来实例化windows
The createWindow()
function loads your web page into a new BrowserWindow instance:createWindow()
函数的作用是:将网页加载到新的BrowserWindow实例中:
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
})
win.loadFile('index.html')
}
Calling your function when the app is ready应用程序准备就绪时调用函数
app.whenReady().then(() => {
createWindow()
})
Many of Electron's core modules are Node.js event emitters that adhere to Node's asynchronous event-driven architecture. Electron的许多核心模块都是遵守Node异步事件驱动架构的Node.js事件发射器。The app module is one of these emitters.应用程序模块就是这些发射器之一。
In Electron, BrowserWindows can only be created after the app module's ready event is fired. 在Electron中,浏览器窗口只能在应用程序模块的ready事件触发后创建。You can wait for this event by using the app.whenReady() API and calling 您可以使用app.whenReady()API等待此事件,并在其承诺实现后调用createWindow()
once its promise is fulfilled.createWindow()
。
You typically listen to Node.js events by using an emitter's 您通常通过使用发射器的.on
function..on
函数来监听Node.js事件。
+ app.on('ready').then(() => {
- app.whenReady().then(() => {
createWindow()
})
However, Electron exposes 然而,Electron公开了app.whenReady()
as a helper specifically for the ready
event to avoid subtle pitfalls with directly listening to that event in particular. app.whenReady()
作为专门针对ready
事件的助手,以避免直接监听该事件时的微妙陷阱。See electron/electron#21972 for details.详见electron/electron#21972。
At this point, running your Electron application's 此时,运行Electron应用程序的start
command should successfully open a window that displays your web page!start
命令应该会成功打开一个显示网页的窗口!
Each web page your app displays in a window will run in a separate process called a renderer process (or simply renderer for short). 应用程序在窗口中显示的每个网页将在一个单独的进程中运行,该进程称为renderer进程(简称为渲染器)。Renderer processes have access to the same JavaScript APIs and tooling you use for typical front-end web development, such as using webpack to bundle and minify your code or React to build your user interfaces.渲染器进程可以访问与典型前端web开发相同的JavaScript API和工具,例如使用webpack捆绑和缩小代码或使用React以构建用户界面。
Managing your app's window lifecycle管理应用程序的窗口生命周期
Application windows behave differently on each operating system. 应用程序窗口在每个操作系统上的行为不同。Rather than enforce these conventions by default, Electron gives you the choice to implement them in your app code if you wish to follow them. 与默认情况下强制执行这些约定不同,如果您希望遵循这些约定,Electron为您提供了在应用程序代码中实现这些约定的选择。You can implement basic window conventions by listening for events emitted by the app and BrowserWindow modules.您可以通过监听应用程序和浏览器窗口模块发出的事件来实现基本窗口约定。
Checking against Node's process.platform variable can help you to run code conditionally on certain platforms. 检查Node的process.platform变量可以帮助您在某些平台上有条件地运行代码。Note that there are only three possible platforms that Electron can run in: 注意,只有三种可能的平台可以运行Electron:win32
(Windows), linux
(Linux), and darwin
(macOS).win32
(Windows)、linux
(linux)和darwin
(macOS)。
Quit the app when all windows are closed (Windows & Linux)关闭所有窗口(windows和Linux)后退出应用程序
On Windows and Linux, closing all windows will generally quit an application entirely. 在Windows和Linux上,关闭所有窗口通常会完全退出应用程序。To implement this pattern in your Electron app, listen for the app module's window-all-closed event, and call app.quit() to exit your app if the user is not on macOS.要在Electron应用程序中实现此模式,请监听应用程序模块的window-all-closed事件,如果用户不在macOS上,则调用app.quit()退出应用程序。
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
Open a window if none are open (macOS)如果没有打开窗口,则打开窗口(macOS)
In contrast, macOS apps generally continue running even without any windows open. 相比之下,macOS应用程序通常会继续运行,即使没有打开任何窗口。Activating the app when no windows are available should open a new one.当没有可用窗口时激活应用程序应打开一个新窗口。
To implement this feature, listen for the app module's activate event, and call your existing 要实现此功能,请侦听应用程序模块的激活事件,如果没有打开浏览器窗口,则调用现有的createWindow()
method if no BrowserWindows are open.createWindow()
方法。
Because windows cannot be created before the 由于无法在ready
event, you should only listen for activate
events after your app is initialized. ready
事件之前创建windows,因此您只能在应用程序初始化后侦听activate
事件。Do this by only listening for activate events inside your existing 通过只监听现有whenReady()
callback.whenReady()
回调中的激活事件来实现这一点。
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
Final starter code最终启动码
- main.js
- index.html
const { app, BrowserWindow } = require('electron');
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
});
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();
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>
Optional: Debugging from VS Code可选:从VS Code进行调试
If you want to debug your application using VS Code, you need to attach VS Code to both the main and renderer processes. 如果希望使用VS Code调试应用程序,则需要将VS Code附加到主进程和渲染器进程。Here is a sample configuration for you to run. 下面是要运行的示例配置。Create a launch.json configuration in a new 在项目中的新.vscode
folder in your project:.vscode
文件夹中创建启动JSON配置:
{
"version": "0.2.0",
"compounds": [
{
"name": "Main + renderer",
"configurations": ["Main", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "pwa-chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "Main",
"type": "pwa-node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": [".", "--remote-debugging-port=9222"],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}
The "Main + renderer" option will appear when you select "Run and Debug" from the sidebar, allowing you to set breakpoints and inspect all the variables among other things in both the main and renderer processes.当您从侧栏中选择“运行和调试”时,将出现“主+渲染器”选项,允许您设置断点并检查主进程和渲染器进程中的所有变量。
What we have done in the 我们在launch.json
file is to create 3 configurations:launch.json
文件中所做的是创建3个配置:
Main
is used to start the main process and also expose port 9222 for remote debugging (用于启动主进程,并公开用于远程调试的端口9222(--remote-debugging-port=9222
).--remote-debugging-port=9222
)。This is the port that we will use to attach the debugger for the这是我们将用于为Renderer
.Renderer
附加调试器的端口。Because the main process is a Node.js process, the type is set to因为主进程是一个Node.js进程,所以类型被设置为pwa-node
(pwa-
is the prefix that tells VS Code to use the latest JavaScript debugger).pwa-node
(pwa-
是告诉VS Code使用最新JavaScript调试器的前缀)。Renderer
is used to debug the renderer process.用于调试渲染器进程。Because the main process is the one that creates the process, we have to "attach" to it (因为主流程是创建流程的流程,我们必须“附加”到它("request": "attach"
) instead of creating a new one."request": "attach"
),而不是创建新流程。The renderer process is a web one, so the debugger we have to use is渲染器进程是web进程,因此我们必须使用的调试器是pwa-chrome
.pwa-chrome
。Main + renderer
is a compound task that executes the previous ones simultaneously.是同时执行先前任务的复合任务。
Because we are attaching to a process in 因为我们在Renderer
, it is possible that the first lines of your code will be skipped as the debugger will not have had enough time to connect before they are being executed. Renderer
中附加了一个进程,所以可能会跳过代码的第一行,因为调试器在执行它们之前没有足够的时间进行连接。You can work around this by refreshing the page or setting a timeout before executing the code in development mode.您可以通过在开发模式下执行代码之前刷新页面或设置超时来解决此问题。
If you want to dig deeper in the debugging area, the following guides provide more information:如果您想深入调试区域,以下指南提供了更多信息:
Summary小结
Electron applications are set up using npm packages. 使用npm包设置Electron应用程序。The Electron executable should be installed in your project's Electron可执行文件应该安装在项目的devDependencies
and can be run in development mode using a script in your package.json file.devDependencies
中,并且可以使用package.json
文件中的脚本在开发模式下运行。
The executable runs the JavaScript entry point found in the 可执行文件运行main
property of your package.json. package.json
的main
属性中的JavaScript入口点。This file controls Electron's main process, which runs an instance of Node.js and is responsible for your app's lifecycle, displaying native interfaces, performing privileged operations, and managing renderer processes.该文件控制Electron的主进程,该进程运行Node.js实例,负责应用程序的生命周期、显示本机接口、执行特权操作和管理渲染器进程。
Renderer processes (or renderers for short) are responsible for display graphical content. 渲染器进程(简称渲染器)负责显示图形内容。You can load a web page into a renderer by pointing it to either a web address or a local HTML file. 通过将网页指向web地址或本地HTML文件,可以将网页加载到渲染器中。Renderers behave very similarly to regular web pages and have access to the same web APIs.呈现器的行为与常规网页非常相似,可以访问相同的web API。
In the next section of the tutorial, we will be learning how to augment the renderer process with privileged APIs and how to communicate between processes.在本教程的下一节中,我们将学习如何使用特权API增强渲染器进程,以及如何在进程之间进行通信。