Electron 一小时快速上手:前端桌面应用开发教程
介绍 Electron 框架的基础概念及快速上手流程。涵盖工程初始化、主进程与渲染进程通信机制、本地文件读写操作以及使用 Electron Forge 进行跨平台打包的方法。通过实际案例演示了如何构建兼容 Windows、Linux 和 macOS 的桌面应用程序,并解决了内容安全策略(CSP)及热更新配置等常见问题。

介绍 Electron 框架的基础概念及快速上手流程。涵盖工程初始化、主进程与渲染进程通信机制、本地文件读写操作以及使用 Electron Forge 进行跨平台打包的方法。通过实际案例演示了如何构建兼容 Windows、Linux 和 macOS 的桌面应用程序,并解决了内容安全策略(CSP)及热更新配置等常见问题。

注意:
简单的一句话,就是用 HTML+CSS+JS+Node.js+(Native API) 做兼容多个系统(Windows、Linux、Mac)的软件。
官网解释如下: Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。嵌入 Chromium 和 Node.js 到二进制的 Electron 允许您保持一个 JavaScript 代码库并创建在 Windows 上运行的跨平台应用 macOS 和 Linux——不需要本地开发经验。
需要 node 和 npm,先检测是否安装。
node -v
npm -v

命令行创建:
mkdir my-electron-app && cd my-electron-app
npm init
或者手动快速创建。
package.json 文件示例:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "test Electron",
"main": "main.js",
"author": "Author Name",
"license": "MIT"
}
有几条规则需要遵循:

npm install --save-dev electron
// 或者
npm install electron -D
在 package.json 配置文件中的 scripts 字段下增加一条 start 命令:
{
"scripts": {
"start": "electron ."
}
}
这一步,完整的 package.json:
{
"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。

在 main.js 里写入:
console.log(123)

再运行!
npm start
注意:

成功输出 123,工程搭建完成。
点击可以查看 browser-window 配置项的开发文档
在 main.js 里写入:
const { app, BrowserWindow } = require('electron')
app.on('ready', () => {
// 当 app 准备好后,执行 createWindow 创建窗口
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true,
x: 0,
y: 0
})
// 加载一个远程页面
win.loadURL('https://example.com')
})
运行:
npm start

成功显示页面。
新建 pages 目录,创建页面,这里就像写前端一样了。index.html, index.css。

index.html 里:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这里是 index 页面</title>
</head>
<body>
<h1>这里是 index 里的标题</h1>
</body>
</html>
main.js 里引入页面:
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')
})
运行:
npm start
有安全提示。

在 index.html 里加入如下代码,内容安全策略警告提示消失。
<!-- electron 提供的配置 -->
<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:';">
关于 CSP 的详细说明:MDN 内容安全策略详解、Electron 安全策略规范
兼容 mac(完善窗口行为)。
在 main.js 里写入兼容各个系统平台的代码:
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()
// 兼容核心代码 1
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// 兼容核心代码 2
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
代码解析:

下面两句代码相等:
app.on('ready', () => { /* 写核心代码 */ })
// 或者
app.whenReady().then(() => { /* 写核心代码 */ })
npm i nodemon -D
package.json 里重写 start:
"start": "nodemon --exec electron ."
这样保存 main.js 里的内容,就自动热更新了。 但是更新 index.html 里的代码不能热更新,要加一个 nodemon.json,需要手动添加:
{
"ignore": ["node_modules", "dist"],
"restartable": "r",
"watch": ["*.*"],
"ext": "html,js,css"
}
增加 nodemon.json,重启生效:
npm start
每个页面程序通过渲染和主进程通信,主进程根据需求调用 Native API 来实现功能。
实际,每个页面和主程序通信时,需要建个桥梁来管理它们的通信,preload.js(自己创建),来管理实现通信。
创建 preload.js 定义桥梁 js
在 main.js 中定义 preload.js 为桥梁
main.js 代码:
const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true,
webPreferences: {
// 在 main.js 中定义 preload.js 为桥梁
preload: path.resolve(__dirname, './preload.js')
}
})
win.loadFile('./pages/index/index.html')
win.openDevTools()
console.log("main.js")
}
app.on('ready', () => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
创建渲染 js
这是自己整理的,整理了前面 45 分钟,再这么整理下去,1000 字也不够用。 直接上代码。
main.js 里的代码
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()
console.log("main.js")
}
app.(, {
()
app.(, {
(.(). === ) ()
})
})
preload.js 里的代码
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
version: process.version,
saveFile: (data) => {
ipcRenderer.send('file-save', data)
},
readFile() {
return ipcRenderer.invoke('file-read')
}
})
render.js 里的代码
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)
}
html 里的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>这里是 index 页面</title>
</head>
<body>
<h1>欢迎学习 Electron 开发!!!</h1>
<button id="btn1">点我</button>
<hr/>
<input type="text" id="inp"/>
<button id="btn2">向 D 盘写入 hello.txt</button>
<hr>
<button id="btn3">读取 hello.txt 的内容</button>
<script type="text/javascript" src=>
目录结构

备注:
npm install electron-builder -D
备注:json 文件不支持注释,使用时请去掉所有注释。
{
"name": "video-tools",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
},
"build": {
"appId": "com.atguigu.video",
"win": {
"icon": "./logo.ico",
"target": [{ "target": "nsis", "arch": ["x64"] }]
},
"nsis": {
根据视频操作未能完成打包,这种方式在打包时访问 GitHub 多次尝试后,决定放弃。
Electron 中文网也推荐使用这种方式。 Electron Forge 是从建项目开始配置的,因为项目已经写完了,所以从第五步开始。 Electron Forge 从第五步开始的文档
npm install --save-dev @electron-forge/cli
npx electron-forge import
这时在 package.json 里会增加一些配置。

不用管。
npm run make
打包完成。
打包后的文件,在根项目下的 out 文件夹里。

可运行程序在
my-electron-app\out\make\squirrel.windows\x64


C:\Users\Administrator\AppData\Roaming 删除 my_electron_app 这个文件夹。然后用 360 安全卫士清理垃圾。然后重启即可。其他 bug
- 软件有卡死的现象。
- 还会自动重启。
- 软件关闭后会自动重启。
- 在输入框中输入内容,写入后可能会卡死,第二次可能无法输入,但可以读取。
- 启动时有点卡。
win.openDevTools(),否则软件会自动打开调试窗口。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online