Electron 前端开发快速入门教程
⚠️注意:
- 原视频打包时,使用 electron-builder 打包,访问 GitHub 可能需要网络环境配置。
- 本笔记使用 Electron Forge 进行打包,更友好。在 Electron 官网中也推荐使用这种方式 👉 Electron Forge Overview
一、Electron 是什么
简单的一句话,就是用 HTML+CSS+JS+Node.js+(Native API) 做兼容多个系统(Windows、Linux、Mac)的软件。
官网解释如下: Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。嵌入 Chromium 和 Node.js 到二进制的 Electron 允许您保持一个 JavaScript 代码库并创建在 Windows 上运行的跨平台应用 macOS 和 Linux——不需要本地开发经验。
二、Electron 流程模型
三、Electron 搭建工程,若成功则输出 123
3.1 准备
可参考 Electron 官网地址 需要 node 和 npm,先检测是否安装。
node -v
npm -v

3.2 项目初始化
命令行创建
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": "Developer",
"license": "MIT"
}
有几条规则需要遵循: entry point 应为 main.js。 author 与 description 可为任意值,但对于应用打包是必填项。

3.3 安装 Electron
npm install --save-dev electron // 或者 npm install electron -D
3.4 配置并启动
在 package.json 配置文件中的 scripts 字段下增加一条 start 命令:
{
"scripts": {
"start": "electron ."
}
}
这一步,完整的 package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "Developer",
"license": "ISC",
"description": "electron test",
"dependencies": {
"electron": "^31.2.0"
}
}
代码解析:

创建 main.js。

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

再运行!
npm start
注意:
- 先创建 main.js,否则报错
- 如果 main.js 里没写输出(或其他代码)则,启动了运行窗口也不容易看出变化

成功输出 123,工程搭建完成。
四、开始制作程序
4.1 案例 1:做一个简易网页程序
点击可以查看,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

成功显示页面。
4.2 案例 2:做一个本地程序(不是远程链接页面,是自己写的页面)
4.2.1 是本地程序,所以创建新的页面
新建 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', () => {
// 当 app 准备好后,执行 createWindow 创建窗口
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true
})
// 引入页面
win.loadFile('./pages/index/index.html')
})
运行
npm start
4.2.2 解决内容安全策略
有安全提示

在 index.html 里加入,如下代码,内容安全策略警告提示消失。
<!-- electron 提供的配置 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<!-- electron 提供的配置 2 引入 js 后也会报错 -->
<meta http-equiv="Content-Security-Policy" content="default-src none">
<!-- 我自己写的组合的配置更全 加了一个 script 的限制 -->
<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 安全策略规范
4.2.3 增加多系统的兼容代码
兼容 mac(完善窗口行为) electron 文档: 关闭所有窗口时退出应用 (Windows & Linux) 如果没有窗口打开则打开一个窗口 (macOS) 在 main.js 里写入兼容各个系统平台的代码
const { app, BrowserWindow } = require('electron')
function createWindow() {
// 当 app 准备好后,执行 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(() => {
// 写核心代码
})
4.2.4 配置自动重启,保存后自动热更新
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
4.2.5 编写页面程序 (写一个应用程序,可以写入任意文字到 hello.txt 里)
页面程序要写在页面里,那它和主进程的关系如图
每个页面程序通过渲染和主进程通信,主进程根据需求调用 Native API 来实现功能。
实际,每个页面和主程序通信时,需要建个桥梁来管理它们的通信,preload.js(自己创建),来管理实现通信。

创建 preload.js 定义桥梁 js

在 main.js 中定义 preload.js 为桥梁

main.js 代码:
const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow() {
// 当 app 准备好后,执行 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()
// 兼容核心代码 1
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
创建渲染 js

定义渲染 js

下面直接上代码。
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() {
// 当 app 准备好后,执行 createWindow 创建窗口
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
alwaysOnTop: true,
webPreferences: {
// 在 main.js 中定义 preload.js 为桥梁
preload: path.resolve(__dirname, './preload.js')
}
})
ipcMain.on('file-save', writeFile)
ipcMain.handle('file-read', readFile)
// 引入页面
win.loadFile('./pages/index/index.html')
win.()
.()
}
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=>
目录结构

备注:
- 代码稍微有点不一样
- 目录结构也有变化,功能是一样的
五、打包
5.1 使用 electron-builder 打包,打包时要访问 github 需要网络环境配置
- 安装 electron-builder:
npm install electron-builder -D
- 在 package.json 中进行相关配置,具体配置如下:
备注:json 文件不支持注释,使用时请去掉所有注释。
package.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 多次尝试后,决定放弃
5.2 使用 Electron Forge 进行打包(使用这种方式打包的)
Electron 中文网也推荐使用这种方式。 Electron Forge 是从建项目开始配置的,因为项目已经写完了,所以从第五步开始。 Electron Forge 从第五步开始的文档
5.2.1 使用 3 条命令,完成打包。
- 运行第 1 条命令
npm install --save-dev @electron-forge/cli
- 运行第 2 条命令
npx electron-forge import
这时在 package.json 里会增加一些配置。

不用管。
- 直接运行第 3 条命令
npm run make
打包完成。
5.2.2 打包后的位置,在根项目下的 out 文件里
打包后的文件,在根项目下的 out 文件夹里

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

5.2.3 看运行效果,终于完成了。
5.2.4 出现的 bug
- 安装一次后再打开会报错,

- 直接删除软件,目录在
C:\Users\Administrator\AppData\Roaming删除 my_electron_app 这个文件夹。然后用系统清理工具清理垃圾。然后重启即可。
其他 bug
- 软件有卡死的现象
- 还会自动重启
- 软件关闭后会自动重启
- 在输入框中输入内容,写入后可能会卡死,第二次可能无法输入,但可以读取。
- 启动时有点卡
六、总结
- 后面有的 bug 可能与 Electron Forge 配置有关。有时间试试,找个解决方案。
- 也有可能与 main.js 写法有关。
- 打包时删除 main.js 里的
win.openDevTools() //自动打开调试窗口否则软件会自动打开调试窗口。
终于完成,虽然有 bug,但能运行起来了。后续找个更好的解决方案。


