跳至主要内容

快速入门

本指南将引导您完成在 Electron 中创建最简单的“Hello World”应用程序的过程,类似于 electron/electron-quick-start

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

先决条件

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

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

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

node -v
npm -v

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

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

创建您的应用程序

搭建项目结构

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

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

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

  • 入口点 应为 main.js
  • 作者描述 可以是任何值,但对于 应用程序打包 是必要的。

您的 package.json 文件应如下所示

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

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

npm install --save-dev electron

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

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

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

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

npm start

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

运行主进程

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

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

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

注意:如果您此时再次运行 start 脚本,您的应用程序将不再抛出任何错误!但是,它还没有执行任何操作,因为我们尚未在 main.js 中添加任何代码。

创建网页

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

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

<!DOCTYPE html>
<html lang="en">
<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'">
<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 手动插入它们。

在浏览器窗口中打开您的网页

现在您有了网页,将其加载到应用程序窗口中。为此,您需要两个 Electron 模块

  • app 模块,它控制应用程序的事件生命周期。
  • BrowserWindow 模块,它创建和管理应用程序窗口。

因为主进程运行 Node.js,所以您可以在 main.js 文件顶部的 CommonJS 模块中导入它们

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

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

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

win.loadFile('index.html')
}

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

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

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

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

管理窗口的生命周期

尽管您现在可以打开浏览器窗口,但您需要一些额外的样板代码才能使其在每个平台上都更像原生应用程序。应用程序窗口在每个操作系统上的行为都不同,Electron 将实现这些约定的责任交给了开发人员。

通常,您可以使用 process 全局对象的 platform 属性来为特定操作系统运行代码。

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

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

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

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

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

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

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

因为在 ready 事件之前无法创建窗口,所以您应该只在应用程序初始化后才侦听 activate 事件。通过从您现有的 whenReady() 回调中附加事件侦听器来执行此操作。

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

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

注意:此时,您的窗口控件应该可以完全正常工作!

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

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

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

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

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

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

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 文档。

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

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 概念

  • __dirname 字符串指向当前正在执行的脚本的路径(在本例中,为项目的根文件夹)。
  • path.join API 将多个路径段连接在一起,创建一个跨所有平台有效的组合路径字符串。

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

奖励:为您的网页内容添加功能

此时,您可能想知道如何为您的应用程序添加更多功能。

对于与您的网页内容的任何交互,您需要向渲染器进程添加脚本。因为渲染器在正常的 Web 环境中运行,所以您可以在 index.html 文件的结束 </body> 标记之前添加一个 <script> 标记,以包含您想要的任何任意脚本

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

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

回顾

按照上述步骤操作后,您应该拥有一个功能齐全的 Electron 应用程序,如下所示

Simplest Electron app

完整的代码如下所示

// 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://mdn.org.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()
}
})

总结我们所做的所有步骤

  • 我们引导了一个 Node.js 应用程序,并将 Electron 作为依赖项添加。
  • 我们创建了一个 main.js 脚本,该脚本运行我们的主进程,该进程控制我们的应用程序并在 Node.js 环境中运行。在此脚本中,我们使用了 Electron 的 appBrowserWindow 模块来创建一个浏览器窗口,该窗口在单独的进程(渲染器)中显示 Web 内容。
  • 为了在渲染器中访问某些 Node.js 功能,我们将一个预加载脚本附加到我们的 BrowserWindow 构造函数。

打包和分发您的应用程序

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

信息

要为 Linux 构建 RPM 包,您需要 安装其所需的系统依赖项

  1. 在您的 package.json 文件中添加说明,否则 rpmbuild 将失败。空白说明无效。

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

    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 命令创建可分发文件

    npm run make

    > [email protected] 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 文件夹,您的软件包将位于其中

    // 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