跳转至

构建你的第一个应用

:::info 跟随教程操作

这是 Electron 教程的第 2 部分。

  1. 先决条件
  2. 构建你的第一个应用
  3. 使用预加载脚本
  4. 添加功能
  5. 打包你的应用程序
  6. 发布与更新

:::

学习目标

在本部分教程中,你将学习如何设置 Electron 项目并编写一个最小的入门应用。学完本节后,你应该能够从终端以开发模式运行一个可用的 Electron 应用。

设置你的项目

:::caution 避免使用 WSL

如果你使用的是 Windows 机器,请在遵循本教程时不要使用 Windows Subsystem for Linux (WSL),因为尝试执行应用程序时你会遇到问题。

:::

初始化 npm 项目

Electron 应用使用 npm 进行脚手架搭建,以 package.json 文件作为入口。首先创建一个文件夹,并在其中使用 npm init 初始化一个 npm 包。

```sh npm2yarn mkdir my-electron-app && cd my-electron-app npm init

此命令将提示你配置 package.json 中的一些字段。就本教程而言,需要遵循以下几条规则:

- _入口点_ 应为 `main.js`(你很快将创建该文件)。
- _author_、_license_ 和 _description_ 可以是任意值,但后续在 [打包][packaging] 时会用到。

:::caution 使用常规的 `node_modules` 文件夹安装依赖

Electron 的打包工具链要求 `node_modules` 文件夹以 npm 安装 Node 依赖的方式实际存在于磁盘上。默认情况下,[Yarn Berry](https://yarnpkg.com/) 和 [pnpm](http://pnpm.io/) 都使用替代安装策略。

因此,如果你使用这些包管理器,则必须在 Yarn 中设置 [`nodeLinker: node-modules`](https://yarnpkg.com/configuration/yarnrc#nodeLinker),或在 pnpm 中设置 [`nodeLinker: hoisted`](https://pnpm.io/settings#nodelinker)。

:::

然后,将 Electron 安装到你的应用的 **devDependencies** 中,devDependencies 是仅在开发时所需的外部包依赖列表,生产环境中不需要。

:::info 为什么 Electron 是开发依赖?

这看起来可能有些反直觉,因为你的生产代码运行的是 Electron APIs。实际上,Electron 的 JavaScript API 绑定到了一个包含其实现的二进制文件。Electron 的打包步骤会处理这个二进制文件的打包,因此无需将其指定为生产依赖。

:::

```sh npm2yarn
npm install electron --save-dev

在初始化包并安装 Electron 之后,你的 package.json 文件应类似如下。现在你应该还有一个 node_modules 文件夹,其中包含 Electron 可执行文件,以及一个 package-lock.json 锁文件,用于指定要安装的确切依赖版本。

package.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"
  }
}

:::info 高级 Electron 安装步骤

如果直接安装 Electron 失败,请参阅我们的高级安装文档,了解下载镜像、代理和故障排除步骤的说明。

:::

添加 .gitignore

.gitignore 文件用于指定哪些文件和目录应避免被 Git 跟踪。你应将 GitHub 的 Node.js gitignore 模板 的副本放入项目的根文件夹,以避免提交项目的 node_modules 文件夹。

运行 Electron 应用

:::tip 延伸阅读

阅读 Electron 的进程模型 文档,以更好地理解 Electron 的多进程如何协同工作。

:::

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

在创建你的第一个 Electron 应用之前,你首先要使用一个简单的脚本,以确保主进程入口点配置正确。在项目根目录下创建一个 main.js 文件,其中包含一行代码:

main.js
console.log('Hello from Electron 👋')

由于 Electron 的主进程是 Node.js 运行时,你可以使用 electron 命令执行任意 Node.js 代码(甚至可以将它用作 REPL)。要执行此脚本,请将 electron . 添加到 package.json 的 scripts 字段中的 start 命令。此命令将告诉 Electron 可执行文件在当前目录中查找主脚本并以开发模式运行。

```json {7} title='package.json' { "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" } }

```sh npm2yarn
npm run start

你的终端应该会输出 Hello from Electron 👋。恭喜,你已经成功在 Electron 中执行了第一行代码!接下来,你将学习如何使用 HTML 创建用户界面,并将其加载到原生窗口中。

将网页加载到 BrowserWindow 中

在 Electron 中,每个窗口都显示一个网页,该网页可以从本地 HTML 文件或远程网址加载。在此示例中,你将加载一个本地文件。首先,在项目根目录下创建一个 index.html 文件,其中包含一个最简单的网页:

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/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 文件的内容。我们将分别解释每个 高亮代码块。

```js {1,3-10,12-14} title='main.js' showLineNumbers 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}

```js title='main.js (Line 1)'
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')

更多信息,请参阅进程模型文档。

:::info Electron 中的 ES 模块

ECMAScript 模块(即使用 import 加载模块) 从 Electron 28 开始已在 Electron 中受支持。你可以在我们的 ESM 指南中了解 Electron 中 ESM 的现状以及如何在应用中使用它们。

:::

编写可复用的函数来实例化窗口

createWindow() 函数会将你的网页加载到一个新的 BrowserWindow 实例中:

main.js (Lines 3-10)
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })

  win.loadFile('index.html')
}

在应用就绪时调用你的函数

```js title='main.js (Lines 12-14)' @ts-type={createWindow:()=>void} app.whenReady().then(() => { createWindow() })

Electron 的许多核心模块都是 Node.js [事件发射器][],遵循 Node 的异步
事件驱动架构。app 模块就是这些发射器之一。

在 Electron 中,只有在 app 模块的 [`ready`][app-ready] 事件
触发后,才能创建 BrowserWindow。你可以使用 [`app.whenReady()`][app-when-ready] API
等待该事件,并在其 Promise 兑现后调用 `createWindow()`。

:::info

你通常通过使用发射器的 `.on` 函数来监听 Node.js 事件。

```diff
+ app.on('ready', () => {
- app.whenReady().then(() => {
  createWindow()
})

然而,Electron 将 app.whenReady() 暴露为一个专门针对 ready 事件的辅助方法, 以避免直接监听该事件时可能出现的细微陷阱。 详细信息请参阅 electron/electron#21972。

:::

此时,运行你的 Electron 应用的 start 命令应该会成功 打开一个显示你网页的窗口!

你的应用在窗口中显示的每个网页都会运行在一个称为 renderer 进程(或简称为 renderer)的独立进程中。Renderer 进程可以访问 你在典型前端 Web 开发中使用的相同 JavaScript API 和工具,例如使用 webpack 来打包和压缩代码,或使用 React 来构建用户界面。

管理应用的窗口生命周期

应用窗口在每个操作系统上的行为都不同。Electron 默认不会 强制实施这些约定,而是让你可以选择在应用代码中实现它们,如果你希望遵循这些约定。你可以通过监听 app 和 BrowserWindow 模块发出的事件来实现基本的窗口 约定。

:::tip 特定进程的控制流

检查 Node 的 process.platform 变量可以帮助你 针对特定平台有条件地运行代码。请注意,Electron 可以运行的平台只有三种:win32(Windows)、linux(Linux) 和 darwin(macOS)。

:::

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

在 Windows 和 Linux 上,关闭所有窗口通常会完全退出应用。 要在你的 Electron 应用中实现这种模式,请监听 app 模块的 window-all-closed 事件,并在用户不在 macOS 上时调用 app.quit() 退出应用。

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

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

相比之下,macOS 应用通常在没有打开任何窗口时也会继续运行。 当没有可用窗口时激活应用,应该会打开一个新窗口。

要实现此功能,请监听 app 模块的 activate 事件,并在没有打开的 BrowserWindow 时调用你现有的 createWindow() 方法。

由于窗口不能在 ready 事件之前创建,因此你只应在应用初始化后监听 activate 事件。可以通过仅在现有的 whenReady() 回调内监听 activate 事件来实现这一点。

```js @ts-type={createWindow:()=>void} app.whenReady().then(() => { createWindow()

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

## 最终起始代码 {#final-starter-code}

```fiddle docs/fiddles/tutorial-first-app

可选:从 VS Code 调试

如果你想使用 VS Code 调试你的应用,你需要将 VS Code 附加到 主进程和渲染进程。下面是一个可供你运行的示例配置。在项目中新建一个 .vscode 文件夹,并创建一个 launch.json 配置:

.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 是一个复合任务,它会同时执行上述配置。

:::caution

由于我们在 Renderer 中附加到进程,你的代码前几行可能会被跳过,因为调试器可能来不及在它们执行之前完成连接。 你可以通过刷新页面,或在开发模式下执行代码之前设置超时来规避此问题。

:::

:::info 延伸阅读

如果你想更深入地了解调试领域,以下指南提供了更多信息:

:::

摘要

Electron 应用程序使用 npm 包进行设置。Electron 可执行文件应安装在项目的 devDependencies 中,并可以使用 package.json 文件中的脚本以开发模式运行。

该可执行文件会运行 package.json 中 main 属性里找到的 JavaScript 入口点。此文件控制 Electron 的主进程,该进程运行一个 Node.js 实例,并负责应用程序的生命周期、显示原生界面、执行特权操作以及管理渲染进程。

渲染进程(或简称渲染器)负责显示图形内容。你可以通过将渲染器指向 Web 地址或本地 HTML 文件,将网页加载到渲染器中。渲染器的行为与普通网页非常相似,并且可以访问相同的 Web API。

在教程的下一节中,我们将学习如何使用特权 API 增强渲染进程,以及进程之间如何通信。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 el/electron