Electron入门(一)

500 阅读6分钟

简介

使用HTML、CSS和JavaScript构建跨平台的桌面应用程序。Electron的本质是结合了Chromium与Node.js。Electron=Chromium + Node.js + Native API

创建应用程序

1.在使用Electron进行开发之前,先安装 Node.js

2.初始化项目

(1) 配置 package.json

npm init

注意:entry point应为main.js;author 、license和 description 可为任意值,但对于应用打包是必填项

(2) 将 Electron 安装为项目的devDependencies,即仅在开发环境需要的额外依赖

npm install electron --save-dev

或者也可以使用下面的命令

npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install --save-dev electron

打包后的应用本身会包含 Electron 的二进制文件,因此不需要将 Electron 作为生产环境依赖

在package.json文件中的scripts字段下增加一条start命令

{
  "name": "first",
  "version": "1.0.0",
  "description": "Hello",
  "main": "main.js",    
  "scripts": {
    "start": "electron .",  // 告诉 Electron 在当前目录下寻找主脚本,并以开发模式运行它
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "abc",
  "license": "ISC",
  "devDependencies": {
    "electron": "^31.1.0"
  }
}

(3) 创建一个名为main.js的文件

console.log('Hello from Electron 👋')

(4) 添加 .gitignore 文件

建议复制一份 GitHub 的 Node.js gitignore 模板(github.com/github/giti…) 到项目的根目录,以避免将node_modules文件夹提交到版本控制系统中。

3.运行Electron应用

npm run start

此时终端会输出 Hello from Electron 👋

4.将网页装载到 BrowserWindow及管理应用的窗口生命周期

在 Electron 中,每个窗口展示一个页面,该页面可以来自本地的 HTML,也可以来自远程 URL。此处以装载本地文件为例

(1) 在项目根目录中创建index.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>

(2) 将网页加载到Electron的BrowsetWindow及管理应用的窗口生命周期

Electron 的许多核心模块都是 Node.js 的事件触发器,遵循 Node.js 的异步事件驱动架构,如app模块。在 Electron 中,只有在 app 模块的 ready事件触发后才能创建 BrowserWindows 实例, 可以通过使用 app.whenReady()API 来监听此事件,并在其成功后调用 createWindow() 方法

Electron应用中的每个页面都在一个单独的进程中运行,我们称这些进程为渲染器renderer ,渲染进程使用与常规Web开发相同的JavaScript API和工具。Electron 目前只支持三个平台:win32(Windows), linux(Linux) 和 darwin (macOS)

在Windows和Linux上,关闭所有窗口通常会完全退出一个应用程序。为了实现这一点,需要监听 app模块的 window-all-closed 事件。如果用户不是在 macOS(darwin) 上运行程序,则调用 app.quit()

macOS 应用通常即使在没有打开任何窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口。为了实现这一特性,监听 app 模块的 activate事件。如果没有任何浏览器窗口是打开的,则调用 createWindow()方法。因为窗口无法在 ready 事件前创建,因此在 whenReady()回调中监听activate事件来完成这个操作

// main.js 
// 使用 CommonJS 语法导入 Electron 模块,app控制应用程序的事件生命周期,BrowserWindow负责创建和管理应用窗口
const { app, BrowserWindow } = require('electron/main')
// 将页面加载到新的 BrowserWindow 实例中
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    // autoHideMenuBar: true,   隐藏菜单栏
    // x: 0,   窗口位置x坐标
    // y: 0,   窗口位置y坐标
    // alwaysOnTop: true    置顶
  })
  win.loadFile('index.html')
}
// 在应用准备就绪时调用函数
app.whenReady().then(() => {
  createWindow()
  // 如果没有窗口打开则打开一个窗口 (macOS)
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})
// 关闭所有窗口时退出应用,不适用macOS (Windows & Linux)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

注意:Electron 遵循 JavaScript 传统约定,以帕斯卡命名法 (PascalCase) 命名可实例化的类 (如 BrowserWindow, Tray 和 Notification),以驼峰命名法 (camelCase) 命名不可实例化的函数、变量等 (如 app, ipcRenderer, webContents)

5.通过预加载脚本从渲染器访问Node.js

Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境,除了 Electron 模块之外,也可以访问 Node.js 内置模块和所有通过 npm 安装的包。而出于安全原因,渲染进程默认跑在网页页面上,并非 Node.js里。因此为了将 Electron 的不同类型的进程桥接在一起,需要使用被称为预加载的特殊脚本

(1) 使用预加载脚本来增强渲染器

BrowserWindow 的预加载脚本运行在具有 HTML DOM 和 Node.js、Electron API 的有限子集访问权限的环境中。从 Electron 20 开始,预加载脚本默认沙盒化 ,不再拥有完整 Node.js 环境的访问权

与 Chrome 扩展的内容脚本(Content Script)类似,预加载脚本在渲染器加载网页之前注入。 如果想为渲染器添加需要特殊权限的功能,可以通过contextBridge接口定义全局对象

A. 新建preload.js。这里通过 versions这一全局变量,将 Electron 的 process.versions 对象暴露给渲染器

// preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', { // 这里相当于是给window设置了属性versions
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,也可以暴露变量
})

B. 为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload传入脚本的路径

// main.js
const path = require('node:path')
//...
const createWindow = () => {
    const win = new BrowserWindow({
        //...
        // 在现有的 BrowserWindow 构造器中将路径中的预加载脚本传入 webPreferences.preload 选项
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    })
    win.loadFile('index.html')
}
//...

C. 新建renderer.js。此时渲染器可以全局访问versions,该变量可以通过window.versions访问,也可以使用versions访问

// renderer.js
const information = document.getElementById('info')
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`

D. 修改index.html,加上一个 id 属性为 info的全新元素,并且引入renderer.js 脚本

// index.html
//...
<body>
    <h1>Hello from Electron renderer!</h1>
    <p>👋</p>
    <p id="info"></p>
    <script src="./renderer.js"></script>
</body>
</html>
//...

此时项目的运行结果为:

微信图片_20240707002604.png (2) 在进程之间通信(IPC)

由于主进程和渲染进程有着完全不同的分工,Electron 应用通常使用预加载脚本来设置进程间通信 (IPC) 接口以在两种进程之间传输任意信息

可以使用 Electron 的 ipcMain模块和 ipcRenderer模块来进行进程间通信。 为了从网页向主进程发送消息,可以使用 ipcMain.handle设置一个主进程处理程序(handler),然后在预处理脚本中暴露一个被称为 ipcRenderer.invoke的函数来触发该处理程序(handler)

// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('versions', {       
  //...
  ping: () => ipcRenderer.invoke('ping')    // 在预处理脚本中设置 invoke 调用
})
// main.js
const { app, BrowserWindow, ipcMain } = require('electron/main')
//...
app.whenReady().then(() => {    
    ipcMain.handle('ping', () => 'pong')    // 在主进程中设置 handle 监听器
    createWindow()
    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow()
        }
    })
})
//...
// renderer.js
//...
const func = async () => {
    const response = await window.versions.ping()
    console.log(response) // 打印 'pong'
}
func()

此时项目的运行结果为:

微信图片_20240707003307.png

打包并分发应用程序

使用Electron Forge进行打包

1.导入项目到 Forge

将 Electron Forge 的 CLI 工具包安装到项目的 devDependencies 依赖中,然后使用现成的转化脚本将项目导入至 Electron Forge

npm install --save-dev @electron-forge/cli
npx electron-forge import

如果npx electron-forge import提示Failed to install modules: ["electron-squirrel-startup"],可以先安装electron-squirrel-startup,再npx electron-forge import

npm install --save-dev electron-squirrel-startup
或 
npm install -g yarn --registry=https://registry.npmmirror.com
yarn add electron-squirrel-startup

转换脚本完成后,Forge 会将一些脚本添加到package.json 文件中

// package.json
//...
"scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
},
//...

2.使用 Forge 的make命令来创建可分发的应用程序

npm run make

注意:package.json的“author”和“description”在打包时是必填内容;项目的绝对路径不能出现中文

生成的out\first-win32-x64\first.exe是应用入口文件,而out\make\squirrel.windows\x64\first-1.0.0 Setup.exe是应用安装包

参考链接

[1] www.electronjs.org/