Electron 快速入门手册

1. 项目初始化

1.1. 基于 Electron Forge

可以基于 Electron Forge 快速搭建一个具有完整构建流水线的 Electron 项目,这也是官方推荐方法。

Electron Forge 是一个用于打包和分发 Electron 应用的全能工具。它将多个单一功能的包组合在一起,创建一个开箱即用的完整构建流水线,包含代码签名、安装程序和制品发布功能。

Electron Forge 是一个用于打包和分发 Electron 应用的全能工具。它将多个单一功能的包组合在一起,创建一个开箱即用的完整构建流水线,包含代码签名、安装程序和制品发布功能。

  1. 创建项目

要开始使用 Electron Forge,我们首先需要使用 create-electron-app 初始化一个新项目,这个命令是 Forge 的 Init 命令的便利包装。

注意事项:Electron Forge 当前仅支持 npm 和 Yarn Classic。如果我们使用的是 Yarn >= 2,请使用 nodeLinker: node-modules 安装模式。

创建项目命令:

javascript 复制代码
npx create-electron-app@latest my-app

如果是网络问题,npx 无法完成最终安装,可以尝试替代方案,使用 pnpm 来完成初始化。

javascript 复制代码
pnpm dlx create-electron-app@latest my-app
  1. 使用模板

Forge 的初始化脚本可以通过 --template=template-name 标志添加额外的模板代码。

模板使用示例:

javascript 复制代码
 npx create-electron-app@latest my-app -- --template=webpack

如果是网络问题,npx 无法完成最终安装,可以尝试替代方案,使用 pnpm 来完成初始化。

javascript 复制代码
pnpm dlx create-electron-app@latest my-app

目前有四个官方模板可供选择:

  • webpack

  • webpack-typescript

  • vite

  • vite-typescript

  1. 自定义模板

如果官方模板无法满足要求,可以定制自己的模板,比如说我们希望客户端使用 React18 开发或者 Vue3 开发,那么我们可以在 Forge 基础上定义对应模板。

我们以 Vue3 为例,React 是一样的,我们前面选择了 vite 作为构建工具,那么我们就需要基于 vite 来配置 vue3 应用的打包构建策略。

安装相关依赖:

javascript 复制代码
npm install vue
npm install --save-dev @vitejs/plugin-vue

修改渲染器 vite 配置 vite.renderer.config.ts:

javascript 复制代码
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

// https://vitejs.dev/config
export default defineConfig({
    plugins: [vue()]
});

修改 index.html:

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World!</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/renderer.js"></script>
  </body>
</html>

增加 App.vue:

html 复制代码
<!--src/app.vue-->
<template>
  <h1>❤️ Hello World!</h1>
  <p>Welcome to your Electron application.</p>
</template>

<script setup>
console.log('👋 This message is being logged by "App.vue", included via Vite');
</script>

修改渲染器 renderer.ts:

javascript 复制代码
// src/renderer.ts

import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');
  1. 启动应用

现在,我们应该有一个名为 my-app 的目录,里面包含创建基本 Electron 应用所需的所有文件。

启动应用命令:

bash 复制代码
cd my-app
npm start

1.2. 基于 Electron-Vite

官网:https://electron-vite.github.io/

Vue: git clone https://github.com/electron-vite/electron-vite-vue.git

React: git clone https://github.com/electron-vite/electron-vite-react.git

基于脚手架创建项目:

javascript 复制代码
npm create vite@latest my-app

启动项目:

javascript 复制代码
# Enter the project to download dependencies and run them
cd my-app
npm install
npm run dev

这个项目架构最大的不同在于,更新相关使用的 electron-updater、构建使用了 electron-builder,过往 Electron 项目,确实在包更新、构建等方面有很多问题需要自己解决,但现在有了 electron forge,推荐用第一种方式管理项目。

1.3. 方案对比

Electron Forge做掉了很多事情,形成大一统。

Electron 社区已经开发出一个丰富的工具生态系统,以处理 Electron 应用分发的各项任务,包括:

  • 应用打包 (electron-packager)

  • 代码签名 (例如 @electron/osx-sign)

  • 创建平台特定的安装程序 (例如 electron-winstaller 或 electron-installer-dmg)

  • 本地 Node.js 模块重建 (electron-rebuild)

  • 通用 macOS 构建 (@electron/universal)

2. 主进程与渲染进程

在Electron中,主进程与渲染进程之间的通信是构建功能丰富桌面应用程序的关键部分。

由于这两个进程在Electron的进程模型中承担不同的责任,进程间通信(IPC)成为了执行许多常见任务的唯一途径,例如从UI调用本地API或通过本地菜单触发网页内容的变化。

2.1. 进程间通信(IPC)

IPC允许主进程与渲染进程之间交换消息。

Electron通过开发者定义的"通道"实现这一功能,使用ipcMain 和ipcRenderer 模块。IPC通道可以随意命名的,也要可以在两个模块中使用相同的通道名称。

我们将通过一些基本的IPC模式与具体示例,学习进程通信实现。

2.2. 理解上下文隔离的进程

在实现细节之前,我们应该了解使用预加载脚本在上下文隔离的渲染进程中导入Node.js和Electron模块的概念:

2.3. 模式一:渲染进程到主进程(单向)

要从渲染进程向主进程发送单向IPC消息,可以使用 ipcRenderer.send API发送消息,然后由 ipcMain.on API接收。

通常,我们会使用此模式在网页中调用主进程API。

我们通过创建一个简单的应用程序来演示,该程序可以通过输入框内容更改窗口标题。

​

  1. 使用 ipcMain.on 监听事件

在主进程中,使用 ipcMain.on API 定义 set-title 设置 IPC 监听:

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');

function handleSetTitle(event, title) {
  const webContents = event.sender;
  const win = BrowserWindow.fromWebContents(webContents);
  win.setTitle(title);
}

function createWindow() {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });
  mainWindow.loadFile('index.html');
}

app.whenReady().then(() => {
  ipcMain.on('set-title', handleSetTitle);
  createWindow();
});

在上面的 handleSetTitle 回调中,有两个参数:IpcMainEvent 结构和一个 title 字符串。

每当通过 set-title 通道传来消息时,此函数将找到与消息发送者关联的 BrowserWindow 实例,调用 win.setTitle API。

  1. 通过预加载脚本暴露 ipcRenderer.send

要向上面创建的监听器发送消息,我们可以使用 ipcRenderer.send API。

默认情况下,渲染进程无法访问 Node.js 或 Electron 模块,我们可以使用预加载脚本来暴露对应 API。

在预加载脚本中,添加以下代码,以将通过全局 window.electronAPI 变量,将方法暴露给渲染进程,以此实现在页面中调用对应方法。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  setTitle: (title) => ipcRenderer.send('set-title', title),
});

此时,我们可以在渲染进程中使用 window.electronAPI.setTitle() 函数了。

  1. 构建渲染进程UI

在加载的HTML文件中,添加一个基本的用户界面,由一个文本输入框和一个按钮组成:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
</head>
<body>
    Title: <input id="title"/>
    <button id="btn" type="button">Set</button>
    <script src="./renderer.js"></script>
</body>
</html>

要使这些元素具有交互性,我们将在导入的 renderer.js 文件中添加几行代码,使用从预加载脚本暴露的 window.electronAPI API,实现调用主进程方法:

javascript 复制代码
const setButton = document.getElementById('btn');
const titleInput = document.getElementById('title');
setButton.addEventListener('click', () => {
    const title = titleInput.value;
    window.electronAPI.setTitle(title);
});

接下来会看到,窗口标题变化了。

2.4. 模式二:渲染进程到主进程(双向)

双向IPC的一个常见应用是从渲染进程代码调用主进程模块并等待结果。这可以通过使用 ipcRenderer.invoke 搭配 ipcMain.handle 来实现。

  1. 使用 ipcMain.handle 监听事件

在主进程中,我们将创建一个 handleFileOpen() 函数,调用 dialog.showOpenDialog 并返回用户选择的文件路径的值。

此函数用作回调,可以通过 ipcRenderer.invoke 在渲染进程触发主进程 dialog:openFile 事件监听。

javascript 复制代码
const { app, BrowserWindow, dialog, ipcMain } = require('electron');
const path = require('node:path');

async function handleFileOpen() {
  const { canceled, filePaths } = await dialog.showOpenDialog({});
  if (!canceled) {
    return filePaths[0];
  }
}

function createWindow() {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });
  mainWindow.loadFile('index.html');
}

app.whenReady().then(() => {
  ipcMain.handle('dialog:openFile', handleFileOpen);
  createWindow();
});
  1. 通过预加载脚本暴露 ipcRenderer.invoke

在预加载脚本中,我们暴露一个 API 调用,返回用于操作打开文件的主进程 API ipcRenderer.invoke('dialog:openFile') 的 openFile 函数。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile'),
});
  1. 构建渲染进程UI

修改 html 文件

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <title>Dialog</title>
</head>
<body>
  <button type="button" id="btn">Open a File</button>
  File path: <strong id="filePath"></strong>
  <script src='./renderer.js'></script>
</body>
</html>

我们用一个按钮来触发预加载脚本暴露的 api,进而调用主进程对应方法:

javascript 复制代码
const btn = document.getElementById('btn');
const filePathElement = document.getElementById('filePath');

btn.addEventListener('click', async () => {
  const filePath = await window.electronAPI.openFile();
  filePathElement.innerText = filePath;
});

2.5. 模式三:主进程到渲染进程

从主进程发送消息到渲染进程时,我们需要指定接收消息的渲染进程。

消息需要通过其 WebContents 实例发送。此 WebContents 实例包含一个 send 方法,可以像 ipcRenderer.send 一样来使用。

  1. 使用 webContents 模块发送消息

本示例中,我们定义一个应用菜单,菜单点击后执行对应逻辑,我们有两个子菜单,分别用来通知渲染进程,数值 +1 和 数值 -1。

javascript 复制代码
const { app, BrowserWindow, Menu, ipcMain } = require('electron');
const path = require('node:path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });

  const menu = Menu.buildFromTemplate([
    {
      label: app.name,
      submenu: [
        {
          click: () => mainWindow.webContents.send('update-counter', 1),
          label: 'Increment',
        },
      ],
    },
    {
      click: () => mainWindow.webContents.send('update-counter', -1),
      label: 'Decrement',
    },
    { type: 'separator' },
    {
      role: 'quit',
    },
  ]);

  Menu.setApplicationMenu(menu);
  mainWindow.loadFile('index.html');
}

app.whenReady().then(createWindow);
  1. 通过预加载脚本暴露 ipcRenderer.on

为了接收从主进程发送的消息,我们需要在预加载脚本中向 window.electronAPI 添加一个事件接收器的暴露 API,确保渲染进程能够响应这些更新。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback),
});
  1. 构建渲染进程UI

最后,在 index.html 文件中创建一个简单的用户界面,显示计数器值:

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <title>Counter</title>
</head>
<body>
  <h1>Counter: <span id="counter">0</span></h1>
  <script src='./renderer.js'></script>
</body>
</html>

然后,在渲染进程脚本中,我们将 window.electronAPI.onUpdateCounter 添加到DOM加载事件,以便及时更新UI:

javascript 复制代码
let counter = 0;
const counterElement = document.getElementById('counter');

window.electronAPI.onUpdateCounter((event, value) => {
  counter += value;
  counterElement.innerText = counter;
});

3. 常用API

在 Electron 33.0.2 中,以下是一些常用的内置组件与 API,供开发者在构建应用程序时使用。

每个模块都附有示例代码,以帮助你更好地理解其用法。

3.1. app 模块

  • app.on(event, callback): 监听应用生命周期事件。
javascript 复制代码
const { app } = require('electron');

app.on('ready', () => {
  console.log('应用已准备就绪');
});
  • app.quit(): 退出应用程序。
javascript 复制代码
app.on('window-all-closed', () => {
  app.quit();
});
  • app.getPath(name): 获取特定路径。
javascript 复制代码
const userDataPath = app.getPath('userData');
console.log('用户数据路径:', userDataPath);

3.2. BrowserWindow 模块

  • new BrowserWindow(options): 创建新的浏览器窗口。
javascript 复制代码
const { BrowserWindow } = require('electron');

let mainWindow = new BrowserWindow({
  width: 800,
  height: 600,
  webPreferences: {
    nodeIntegration: true
  }
});

mainWindow.loadURL('https://example.com');

3.3. ipcMain 和 ipcRenderer 模块

  • ipcMain.on(channel, listener): 主进程监听消息。
javascript 复制代码
const { ipcMain } = require('electron');

ipcMain.on('message-from-renderer', (event, arg) => {
  console.log('接收到渲染进程的消息:', arg);
});
  • ipcRenderer.send(channel, data): 渲染进程发送消息。
javascript 复制代码
const { ipcRenderer } = require('electron');

ipcRenderer.send('message-from-main', 'Hello from renderer!');
  • Menu.buildFromTemplate(template): 从模板创建菜单。
javascript 复制代码
const { Menu } = require('electron');

const menuTemplate = [
  {
    label: '文件',
    submenu: [
      { role: 'quit' }
    ]
  }
];

const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);

3.5. dialog 模块

  • dialog.showOpenDialog(options): 显示打开文件对话框。
javascript 复制代码
const { dialog } = require('electron');

dialog.showOpenDialog({
  properties: ['openFile']
}).then(result => {
  console.log('选择的文件路径:', result.filePaths);
});

3.6. shell 模块

  • shell.openPath(path): 打开指定路径的文件。
javascript 复制代码
const { shell } = require('electron');

shell.openPath('/path/to/file.txt');

3.7. webContents 模块

  • webContents.send(channel, ...args): 从主进程向渲染进程发送消息。
javascript 复制代码
mainWindow.webContents.send('message-from-main', 'Hello from main!');

3.8. nativeImage 模块

  • nativeImage.createFromPath(path): 从文件路径创建图像对象。
javascript 复制代码
const { nativeImage } = require('electron');

const image = nativeImage.createFromPath('/path/to/image.png');

3.9. session 模块

  • session.defaultSession: 获取默认会话的实例。
javascript 复制代码
const { session } = require('electron');

const defaultSession = session.defaultSession;

3.10. clipboard 模块

  • clipboard.writeText(text): 将文本写入剪贴板。
javascript 复制代码
const { clipboard } = require('electron');

clipboard.writeText('Hello, clipboard!');
  • clipboard.readText(): 从剪贴板读取文本。
javascript 复制代码
const text = clipboard.readText();
console.log('剪贴板内容:', text);

4. 应用构建发布

我们可以通过 Electron Forge 进行应用分发。

Electron Forge 是一个为 Electron 应用提供完整构建和发布工作流的工具。以下是构建和发布应用的详细步骤,以及不同构建器的功能介绍。

正常发布到商店,我们需要签名,苹果证书的获取方式请点击链接查看。

4.1. 构建与发布概述

  1. 构建应用:通过 Electron Forge 构建应用,可以使用以下命令:
javascript 复制代码
npm run make
  1. 发布应用:发布过程通常涉及将构建好的应用上传到合适的发布平台,例如 GitHub。

4.2. 图标配置

参考链接

4.3. 构造器

AppX

  • 作用:用于在 Windows 上创建 Microsoft Store 应用包。

  • 特点:支持 UWP(通用 Windows 平台),便于在 Microsoft Store 发布。

deb

  • 作用:用于在基于 Debian 的 Linux 发行版(如 Ubuntu)上创建安装包。

  • 特点:使用 dpkg 安装,支持依赖管理。

DMG

  • 作用:在 macOS 上创建可安装的磁盘映像。

  • 特点:用户友好的安装过程,用户可以将应用拖放到应用程序文件夹。

Flatpak

  • 作用:用于在 Linux 上创建跨发行版的应用包。

  • 特点:提供沙箱环境,确保应用的安全性和隔离性。

pkg

  • 作用:用于在 macOS 上创建可执行的安装包。

  • 特点:支持多种安装选项,可简化用户安装流程。

RPM

  • 作用:在基于 Red Hat 的 Linux 发行版(如 Fedora)上创建安装包。

  • 特点:与 yum 和 dnf 兼容,支持依赖管理。

Snapcraft

  • 作用:用于在 Linux 上创建 Snap 包。

  • 特点:提供自动更新功能,支持多种 Linux 发行版。

Squirrel.Windows

  • 作用:用于创建 Windows 应用的安装包。

  • 特点:支持增量更新,适合快速迭代的应用。

WiX MSI

  • 作用:用于在 Windows 上创建安装程序。

  • 特点:生成标准的 Windows 安装包,支持复杂的安装逻辑。

ZIP

  • 作用:将应用打包成 ZIP 文件,供用户手动解压和运行。

  • 特点:简单易用,适合小型或非正式分发。

4.4. 发布平台

在构建完成后,可以使用 electron-forge 中的 publisher 功能将应用发布到众多平台上,一般我们推荐 GitHub。除了 github 以外,还有很多分发平台,以下是几个常用的发布平台及其详细介绍:

Bitbucket

  • 概述:Bitbucket 是 Atlassian 提供的代码托管平台,支持 Git 和 Mercurial。

  • 特点:

    • 团队协作:适合团队项目,提供 Pull Request 和代码审查功能。

    • 集成:与其他 Atlassian 产品(如 Jira 和 Confluence)无缝集成。

    • 发布:可以通过 API 将构建好的应用发布到 Bitbucket 仓库。

Electron Release Server

  • 概述:Electron Release Server 是一个专门为 Electron 应用构建的发布解决方案。

  • 特点:

    • 简化发布流程:自动处理版本管理和发布流程。

    • 支持多平台:可以管理 Windows、macOS 和 Linux 的应用版本。

    • 集成工具:与 Electron Forge 和其他构建工具兼容,便于使用。

GitHub

  • 概述:GitHub 是最流行的代码托管平台,使用 Git 进行版本控制。

  • 特点:

    • 社区支持:拥有庞大的开发者社区,便于项目共享和协作。

    • 发布功能:可以创建 Releases,方便用户下载特定版本的应用。

    • 自动化:可以使用 GitHub Actions 实现持续集成和持续发布(CI/CD)。

Google Cloud Storage

  • 概述:Google Cloud Storage 是 Google 提供的对象存储服务。

  • 特点:

    • 高可用性:数据存储在 Google 的全球基础设施中,提供高可用性和可靠性。

    • 灵活性:支持多种存储类型,适合不同需求。

    • 访问控制:提供细粒度的权限控制,确保安全性。

Nucleus

  • 概述:Nucleus 是一个专注于 Electron 应用发布和更新的服务。

  • 特点:

    • 自动更新:提供内置的自动更新功能,简化用户体验。

    • 多平台支持:支持在多个平台上发布应用,包括 Windows 和 macOS。

    • 管理界面:用户友好的管理界面,便于监控和管理发布。

S3 (Amazon Simple Storage Service)

  • 概述:Amazon S3 是 AWS 提供的对象存储服务,适合大规模数据存储。

  • 特点:

    • 高扩展性:支持大规模存储,适合处理海量数据。

    • 安全性:提供多种安全选项,包括加密和访问控制。

    • 集成:可以与 AWS 的其他服务(如 CloudFront)集成,提供快速的内容分发。

Snapcraft

  • 概述:Snapcraft 是 Canonical 提供的服务,用于构建和发布 Snap 包。

  • 特点:

    • 跨平台:支持在多种 Linux 发行版上安装和运行应用。

    • 安全性:Snap 包提供沙箱环境,增加应用的安全性。

    • 自动更新:支持自动更新功能,简化用户维护。

通过选择合适的发布平台,开发者可以更高效地管理应用的发布和更新过程,提升用户体验。每种平台都有其独特的优势,选择时可根据项目需求和目标用户群体进行综合考虑。

4.5. GitHub Publisher 详解

以下是使用 GitHub 作为发布平台的详细步骤:

  1. 配置 package.json

在您的项目的 package.json 文件中添加以下配置,提供 GitHub 的发布信息:

bash 复制代码
"publish": {
  "provider": "github",
  "owner": "你的GitHub用户名",
  "repo": "你的GitHub仓库名"
}
  1. 生成 GitHub Token
  • 访问 GitHub 设置,创建一个新的个人访问令牌(Personal Access Token)。

  • 给予 repo 权限,以便您可以发布到您的仓库。

  1. 设置环境变量

在命令行中设置 GITHUB_TOKEN 环境变量,以便 Electron Forge 使用您的 GitHub Token:

javascript 复制代码
export GITHUB_TOKEN=你的GitHub令牌
  1. 运行发布命令

使用以下命令将构建的应用发布到 GitHub:

javascript 复制代码
npm run publish
  1. 检查发布结果

发布成功后,您可以在您的 GitHub 仓库的 Releases 页面查看发布的应用。

如若后续应用发布与其他人安装,还需要应用签名,签名需要证书,这里不展开了,详细内容见:
https://www.electronjs.org/docs/latest/tutorial/code-signing

5. 补充资料

相关推荐
mont evergreen32 分钟前
一封从外部世界寄来的信:二维码数据如何穿越三重门,抵达你的屏幕
electron·vue·事件
fellow9921 小时前
DSH Desktop:把 DeepSeek Harness 变成真正的桌面应用
electron·agent·ai编程·deepseek
CDwenhuohuo2 天前
electron pc项目打包成桌面端
前端·javascript·electron
不可能片场3 天前
命令行中文变问号 我用环境变量救了场
前端·electron
不可能片场3 天前
AI视频口型对齐 用音频驱动说话镜头
前端·electron
Zoom4 天前
【开源】7 天,我用 Rust 复刻了 FinalShell!Rhost v1.0.0 发布
rust·electron·shell
爱丶不疚5 天前
Electron: Sentry 都采集了什么?你知道吗
前端·electron
格数致用5 天前
03- Luckysheet与Vue3+Electron集成实战指南
electron·vue3·luckysheet
fellow997 天前
Electron + pnpm 在鸿蒙应用里跑起来
electron·harmonyos·openharmony·deepseek·deepseekharness
记得开心一点嘛7 天前
Trellora:基于 Electron、React 和本地知识库的 AI 知识工作台
人工智能·react.js·electron