Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台的桌面应用程序。它基于 Node.js 和 Chromium,被 Atom 编辑器和许多其他应用程序使用。
Electron 兼容 Mac、Windows 和 Linux,可以构建出三个平台的应用程序。

安装Electron

1
2
3
4
# 新建项目需要, 不新建可以忽视
npm init -y
npm install electron --save-dev
npm install electron-builder --save-dev

安装碰到长时间拉不下来的问题请换源

1
2
3
registry=https://registry.npmmirror.com
electron_mirror=https://cdn.npmmirror.com/binaries/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

在 npm 用户文件夹下 .npmrc 文件中添加, 或者执行以下命令基本就能看到该文件的地址并直接修改文件

1
npm config edit

以下是配置package.json 文件信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
"name": "exe-test",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron . --inspect",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"build": {
"appId": "com.example.mywebapp",
"productName": "test",
"win": {
"target": [
"nsis"
]
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"directories": {
"buildResources": "build-resources"
},
"asarUnpack": [
"main.js",
"preload.js"
],
"files": [
"main.js",
"index.html",
"renderer.js",
"package.json",
"node_modules/**/*",
"assets/**/*",
"config.json"
],
"asar": true
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@electron-forge/maker-squirrel": "^7.4.0",
"electron": "^31.3.1",
"electron-builder": "^24.13.3",
"electron-packager": "^17.1.2"
}
}

main.js

package.json 中定义的入口被 称为主进程。 在主进程中实例化 BrowserWindow 创建的
Web 页面被称为渲染进程。主进程只有一个 渲染进程可以有多个 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// main.js
const { app, BrowserWindow, globalShortcut, ipcMain } = require('electron')
const fs = require('fs');
const path = require('path')
let mainWindow

function createWindow () {
// 读取配置文件
const configPath = path.join(__dirname, 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
mainWindow = new BrowserWindow({
// width: 800,
// height: 600,
fullscreen: true,
autoHideMenuBar: true, // 设置菜单栏自动隐藏
skipTaskbar: true, // 设置窗口不显示在任务栏上
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
},
alwaysOnTop: true, // 设置窗口始终位于顶层
})

// 加载外部网站
mainWindow.loadURL(config.url)

// 注册全局快捷键
// registerGlobalShortcuts()

mainWindow.on('closed', function () {
mainWindow = null
})
mainWindow.setMenu(null);

// 监听来自渲染进程的消息
ipcMain.on('close-app', (event) => {
app.quit(); // 退出整个应用
});
}

function registerGlobalShortcuts() {
// 注册全局快捷键
const isRegisteredCtrlShiftI = globalShortcut.register('CommandOrControl+Shift+I', () => {
console.log('Ctrl+Shift+I is disabled');
});

const isRegisteredF12 = globalShortcut.register('F12', () => {
console.log('F12 is disabled');
});

if (!isRegisteredCtrlShiftI || !isRegisteredF12) {
console.error('Failed to register one or more global shortcuts.');
}

// 当应用关闭时,注销所有全局快捷键
app.on('before-quit', () => {
globalShortcut.unregisterAll();
});
}

app.whenReady().then(createWindow)

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

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

preload.js

preload.js 是一个特殊的脚本文件,它在渲染进程(即 Electron 的网页页面)加载之前运行,并且在渲染进程的上下文中执行。这意味着它具有访问 Node.js 模块和 Electron 远程对象的能力,同时还能访问渲染进程的 DOM API。
preload.js 的主要作用包括:

  • 暴露 Node.js 功能给渲染进程:通过在 preload.js 中使用 exposeInMainWorld 方法,开发者可以将 Node.js 的模块或 Electron 的功能安全地暴露给渲染进程的页面。这样,渲染进程就可以像使用前端库一样使用这些功能,而不需要直接操作 Electron 的远程对象。
  • 提供安全的上下文环境:preload.js 运行在一个特殊的上下文中,它与页面的普通 JavaScript 环境是隔离的。这样可以防止页面脚本直接访问 Node.js 的某些敏感功能,增强了应用的安全性。
  • 初始化全局状态或功能:在 preload.js 中,可以初始化一些全局的状态或功能,这些状态或功能可以在渲染进程中的任何页面或组件中使用。
  • 模块化管理:preload.js 可以作为模块化管理的入口,将一些通用的逻辑或工具函数预先加载到渲染进程中,以便在不同的页面或组件中复用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}

for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})

// preload.js
window.closeApp = function() {
// 发送关闭信号给主进程
require('electron').ipcRenderer.send('close-app');
}

config.json

1
2
3
{
"url": "http://127.0.0.1/"
}

index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<h1 id="chrome-version"></h1>
<h1 id="node-version"></h1>
<h1 id="electron-version"></h1>
</body>
</html>