跳到主要内容Electron 桌面应用开发实战:环境搭建、进程通信与打包 | 极客日志JavaScriptNode.js大前端
Electron 桌面应用开发实战:环境搭建、进程通信与打包
用 Electron 搭建一个带文件读写功能的桌面应用,涵盖环境配置、窗口创建、IPC 通信以及 Electron Forge 打包,分享实际开发中遇到的坑和解决方法。
Electron 桌面应用开发实战笔记
Electron 让我们可以用 HTML、CSS 和 JavaScript 构建跨平台的桌面应用。它把 Chromium 和 Node.js 嵌入二进制包,你只需维护一套前端代码就能跑在 Windows、macOS 和 Linux 上。


它的进程模型很简单:主进程负责创建窗口和管理系统交互,渲染进程跑页面。两者通过预加载脚本(preload)和 IPC 通信。

从零搭建 Electron 项目
先确保装了 Node.js 和 npm:
node -v
npm -v

新建项目并初始化:
mkdir my-electron-app && cd my-electron-app
npm init
package.json 里要指定入口文件 main.js,author 和 description 也要填,否则后面打包会报错。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "electron test",
"main"
:
"main.js"
,
"author"
:
"bin9153"
,
"license"
:
"ISC"
}
npm install --save-dev electron
在 package.json 的 scripts 里加一条启动命令,同时补充 dependencies 或 devDependencies:
{
"name": "my-electron-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "bin9153",
"license": "ISC",
"description": "electron test",
"dependencies": {
"electron": "^31.2.0"
}
}
创建 main.js,先写个简单的 console.log(123) 验证环境。
终端输出 123,表示 Electron 跑起来了。
创建第一个窗口
现在让 Electron 打开一个窗口。在 main.js 里用 BrowserWindow 加载远程页面试试。
const { app, BrowserWindow } = require('electron')
app.on('ready', () => {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true,
x: 0,
y: 0
})
win.loadURL('https://example.com')
})
加载本地页面
自己写的页面更有用。建个 pages/index 目录,里面放 index.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron App</title>
</head>
<body>
<h1>欢迎学习 Electron</h1>
</body>
</html>
修改 main.js,用 loadFile 加载本地页面:
const { app, BrowserWindow } = require('electron')
app.on('ready', () => {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true
})
win.loadFile('./pages/index/index.html')
})
启动后可能会看到安全警告。加上内容安全策略 <meta> 标签就能消除:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:">
兼容不同平台
为了在 macOS 上行为正常,补充窗口生命周期逻辑:
const { app, BrowserWindow } = require('electron')
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true
})
win.loadFile('./pages/index/index.html')
}
app.on('ready', () => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
app.on('ready', callback) 可以写作 app.whenReady().then(callback),两种写法等价。
开发中热更新
每次改代码都要重启很麻烦,用 nodemon 自动重启。
package.json 里的 start 改成:
"start": "nodemon --exec electron ."
再新建一个 nodemon.json,监听 HTML、JS、CSS 变动:
{
"ignore": ["node_modules", "dist"],
"restartable": "r",
"watch": ["*.*"],
"ext": "html,js,css"
}
现在保存 main.js 或页面文件都会自动重载。不过页面内嵌的 HTML 改动有时不会触发,需要手动刷新窗口。
进程间通信:读写本地文件
做一个简单的工具:在页面上输入文字,点击按钮写入 D:/hello.txt,再读回来并弹窗显示。
架构:渲染进程通过预加载脚本(preload.js)暴露的 API 与主进程通信,主进程负责实际的 Node.js 操作(fs 读写)。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
version: process.version,
saveFile: (data) => {
ipcRenderer.send('file-save', data)
},
readFile() {
return ipcRenderer.invoke('file-read')
}
})
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const fs = require('fs')
function writeFile(_, data) {
fs.writeFileSync('D:/hello.txt', data)
}
function readFile() {
const res = fs.readFileSync("D:/hello.txt").toString()
return res
}
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true,
webPreferences: {
preload: path.resolve(__dirname, './preload.js')
}
})
ipcMain.on('file-save', writeFile)
ipcMain.handle('file-read', readFile)
win.loadFile('./pages/index/index.html')
win.openDevTools()
}
app.on('ready', () => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>文件读写 Demo</title>
</head>
<body>
<h1>Electron 文件读写</h1>
<button id="btn1">显示 Node 版本</button>
<hr/>
<input type="text" id="inp"/>
<button id="btn2">写入 hello.txt</button>
<hr>
<button id="btn3">读取 hello.txt</button>
<script src="./render.js"></script>
</body>
</html>
const btn1 = document.getElementById("btn1")
const btn2 = document.getElementById("btn2")
const btn3 = document.getElementById("btn3")
const input = document.getElementById("inp")
btn1.onclick = () => alert(myAPI.version)
btn2.onclick = () => myAPI.saveFile(input.value)
btn3.onclick = async () => {
const res = await myAPI.readFile()
alert(res)
}
现在运行 npm start,就能在窗口里写入文件并读回了。
打包分发
开发完成后需要打包成可执行文件。最早尝试了 electron-builder,但因为网络问题下载依赖一直失败,配置也好几处踩坑。官方文档推荐改用 Electron Forge,实测顺畅很多。
因为我们的项目是手动搭建的,不是用 forge 初始化的,所以从 Forge 的'打包已有项目'第五步开始。
npm install --save-dev @electron-forge/cli
npx electron-forge import
npm run make
electron-forge import 会自动向 package.json 添加 makder 配置,无需手动修改。
打包成功后,在项目根目录的 out 文件夹里就能找到安装包。
Windows 下 exe 位于 my-electron-app\out\make\squirrel.windows\x64。
遇到的坑
- 打包后首次运行正常,但再次打开会报错。可以到
C:\Users\Administrator\AppData\Roaming 下删除 my_electron_app 文件夹,再用清理工具扫一下,重启就行。
- 软件偶尔卡死、自动重启,可能和进程处理或 forge 默认配置有关。我也遇到过输入框写入后卡死、第二次无法输入但能读取的情况。
- 打包前记得把
main.js 里的 win.openDevTools() 注释掉,不然用户打开软件会弹出调试窗口。
- Electron Forge 打包的 bug 不一定是代码问题,有时跟 Squirrel 安装器机制有关。后续可以研究下 maker 的配置项。
总结
从搭建环境、创建窗口到进程通信、最终打包,整体流程跑下来并不复杂。如果你熟悉前端开发,用 Electron 做小工具的门槛很低。进程间的安全通信和打包细节是容易踩坑的点,提前走过一遍能省不少时间。这个笔记只覆盖了很基础的部分,后续可以继续往系统托盘、自动更新、多窗口管理等方向扩展。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online