构建你的第一个应用
学习目标
在本教程的这一部分中,你将学习如何设置你的 Electron 项目并编写一个最小的启动应用。 在本节结束时,你能够从你的终端以开发模式运行一个可用的 Electron 应用。
设置你的项目
如果你使用的是 Windows 机器,请在学习本教程时不要使用 适用于 Linux 的 Windows 子系统 (WSL),因为在尝试执行应用程序时会遇到问题。
初始化你的 npm 项目
Electron 应用是使用 npm 搭建的,其中 package.json 文件作为入口点。 首先创建一个文件夹,并使用 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
此命令将提示你配置 package.json 中的一些字段。 为了本教程的目的,需要遵循一些规则
- 入口点应为
main.js
(你将很快创建该文件)。 - 作者、许可证 和 描述 可以是任何值,但对于稍后的 打包 是必需的。
然后,将 Electron 安装到你应用的 devDependencies 中,这是仅在开发期间需要的外部程序包依赖项列表,在生产环境中不需要。
这可能看起来违反直觉,因为你的生产代码正在运行 Electron API。 但是,打包的应用将与 Electron 二进制文件捆绑在一起,从而无需将其指定为生产依赖项。
- npm
- Yarn
npm install electron --save-dev
yarn add electron --dev
初始化你的包并安装 Electron 后,你的 package.json 文件应如下所示。 你现在还应该有一个包含 Electron 可执行文件的 node_modules
文件夹,以及一个指定要安装的确切依赖项版本的 package-lock.json
锁文件。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
如果直接安装 Electron 失败,请参阅我们的高级安装文档,以获取有关下载镜像、代理和故障排除步骤的说明。
添加 .gitignore
.gitignore
文件指定了要避免使用 Git 跟踪的文件和目录。 你应该将 GitHub 的 Node.js gitignore 模板 的副本放入你的项目的根文件夹中,以避免提交你的项目的 node_modules
文件夹。
运行 Electron 应用
阅读 Electron 的进程模型 文档,以更好地理解 Electron 的多个进程如何协同工作。
你在 package.json 中定义的 main
脚本是任何 Electron 应用程序的入口点。 此脚本控制主进程,该进程在 Node.js 环境中运行,并负责控制你应用的生命周期、显示原生界面、执行特权操作以及管理渲染进程(稍后会详细介绍)。
在创建你的第一个 Electron 应用之前,你将首先使用一个简单的脚本来确保正确配置了你的主进程入口点。 在你的项目的根文件夹中创建一个 main.js
文件,其中包含一行代码
console.log('Hello from Electron 👋')
由于 Electron 的主进程是一个 Node.js 运行时,因此你可以使用 electron
命令执行任意 Node.js 代码 (你甚至可以将其用作 REPL)。 要执行此脚本,请将 electron .
添加到你的 package.json 的 scripts
字段中的 start
命令。 此命令将告诉 Electron 可执行文件在当前目录中查找主脚本,并在开发模式下运行它。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
- npm
- Yarn
npm run start
yarn run start
你的终端应打印出 Hello from Electron 👋
。 恭喜你,你已经在 Electron 中执行了你的第一行代码! 接下来,你将学习如何使用 HTML 创建用户界面,并将其加载到原生窗口中。
将网页加载到 BrowserWindow 中
在 Electron 中,每个窗口都会显示一个网页,该网页可以从本地 HTML 文件或远程网址加载。 对于此示例,你将加载一个本地文件。 首先在你的项目的根文件夹中的 index.html
文件中创建一个基本的网页
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://mdn.org.cn/en-US/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>
现在你有一个网页,你可以将其加载到 Electron BrowserWindow 中。 将你的 main.js
文件的内容替换为以下代码。 我们将分别解释每个突出显示的代码块。
const { app, BrowserWindow } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
导入模块
const { app, BrowserWindow } = require('electron')
在第一行中,我们使用 CommonJS 模块语法导入了两个 Electron 模块
- app,它控制你应用的事件生命周期。
- BrowserWindow,它创建和管理应用窗口。
模块大写约定
你可能已经注意到 app 和 BrowserWindow 模块之间的大小写差异。 Electron 在这里遵循典型的 JavaScript 约定,其中 PascalCase 模块是可实例化的类构造函数 (例如,BrowserWindow, Tray, Notification),而 camelCase 模块不可实例化 (例如,app, ipcRenderer, webContents)。
类型化的导入别名
为了在编写 TypeScript 代码时获得更好的类型检查,你可以选择从 electron/main
导入主进程模块。
const { app, BrowserWindow } = require('electron/main')
有关更多信息,请参阅 进程模型文档。
ECMAScript 模块 (即使用 import
来加载模块) 从 Electron 28 开始在 Electron 中受支持。 你可以在 我们的 ESM 指南 中找到有关 Electron 中 ESM 状态以及如何在我们的应用中使用它们的更多信息。
编写一个可重用的函数来实例化窗口
createWindow()
函数将你的网页加载到新的 BrowserWindow 实例中
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
当应用准备就绪时调用你的函数
app.whenReady().then(() => {
createWindow()
})
Electron 的许多核心模块都是 Node.js 事件发射器,它们遵循 Node 的异步事件驱动架构。 app 模块就是其中一个发射器。
在 Electron 中,只有在 app 模块的 ready
事件触发后才能创建 BrowserWindow。 你可以使用 app.whenReady()
API 等待此事件,并在其 Promise 完成后调用 createWindow()
。
你通常可以通过使用发射器的 .on
函数来侦听 Node.js 事件。
+ app.on('ready', () => {
- app.whenReady().then(() => {
createWindow()
})
但是,Electron 公开了 app.whenReady()
作为专门针对 ready
事件的帮助程序,以避免直接侦听该事件时出现的细微缺陷。 有关详细信息,请参阅 electron/electron#21972。
此时,运行你的 Electron 应用程序的 start
命令应该能够成功打开一个显示你的网页的窗口!
你的应用在窗口中显示的每个网页都将在一个单独的进程中运行,该进程称为渲染器进程 (或简称为 渲染器)。 渲染器进程可以访问与你用于典型前端 Web 开发的相同的 JavaScript API 和工具,例如使用 webpack 打包和缩小你的代码或使用 React 构建你的用户界面。
管理你应用的窗口生命周期
应用程序窗口在每个操作系统上的行为都不同。 Electron 不会默认强制执行这些约定,而是让你选择在你的应用代码中实现它们 (如果你希望遵循它们)。 你可以通过侦听 app 和 BrowserWindow 模块发出的事件来实现基本的窗口约定。
检查 Node 的 process.platform
变量可以帮助你在某些平台上有条件地运行代码。 请注意,Electron 只能在三个可能的平台上运行:win32
(Windows)、linux
(Linux) 和 darwin
(macOS)。
当所有窗口都关闭时退出应用 (Windows 和 Linux)
在 Windows 和 Linux 上,关闭所有窗口通常会完全退出应用程序。 要在你的 Electron 应用中实现此模式,请侦听 app 模块的 window-all-closed
事件,并调用 app.quit()
以退出你的应用 (如果用户不在 macOS 上)。
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
如果未打开任何窗口,则打开一个窗口 (macOS)
相反,即使没有任何窗口打开,macOS 应用通常也会继续运行。 当没有窗口可用时激活应用应该打开一个新的窗口。
要实现此功能,请侦听 app 模块的 activate
事件,并且如果没有打开任何 BrowserWindow,请调用你现有的 createWindow()
方法。
由于无法在 ready
事件之前创建窗口,因此你只应在你的应用初始化后侦听 activate
事件。 通过仅在你现有的 whenReady()
回调中侦听激活事件来执行此操作。
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
最终启动代码
- main.js
- index.html
const { app, BrowserWindow } = require('electron/main')
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>
可选: 从 VS Code 调试
如果你想使用 VS Code 调试你的应用程序,你需要将 VS Code 附加到主进程和渲染器进程。 这里有一个示例配置供你运行。 在你的项目中的一个新的 .vscode
文件夹中创建一个 launch.json 配置
{
"version": "0.2.0",
"compounds": [
{
"name": "Main + renderer",
"configurations": ["Main", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "Main",
"type": "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"
}
]
}
当你从侧边栏选择 "运行和调试" 时,将显示 "Main + renderer" 选项,允许你设置断点并检查主进程和渲染器进程中的所有变量。
我们在 launch.json
文件中所做的是创建 3 个配置
Main
用于启动主进程,并公开端口 9222 以进行远程调试 (--remote-debugging-port=9222
)。 这是我们将用于附加Renderer
调试器的端口。 由于主进程是 Node.js 进程,因此类型设置为node
。Renderer
用于调试渲染器进程。 由于主进程是创建该进程的进程,因此我们必须 "附加" 到它 ("request": "attach"
) 而不是创建一个新的进程。 渲染器进程是一个 Web 进程,因此我们必须使用的调试器是chrome
。Main + renderer
是一个 复合任务,它同时执行前面的任务。
由于我们在 Renderer
中附加到一个进程,因此可能会跳过你的代码的第一行,因为调试器没有足够的时间在执行它们之前连接。 你可以通过刷新页面或在开发模式下执行代码之前设置超时来解决此问题。
如果你想深入研究调试领域,以下指南提供了更多信息
概要
Electron 应用程序是使用 npm 包进行设置的。 Electron 可执行文件应安装在项目的 devDependencies
中,并且可以使用 package.json 文件中的脚本在开发模式下运行。
该可执行文件运行 package.json 文件中 main
属性指定的 JavaScript 入口点。此文件控制 Electron 的 主进程,该进程运行 Node.js 的一个实例,并负责应用程序的生命周期、显示原生界面、执行特权操作以及管理渲染器进程。
渲染器进程(简称渲染器)负责显示图形内容。 你可以将一个网页加载到渲染器中,方法是将其指向一个网址或一个本地 HTML 文件。 渲染器的行为与常规网页非常相似,并且可以访问相同的 Web API。
在本教程的下一部分,我们将学习如何使用特权 API 增强渲染器进程,以及如何在进程之间进行通信。