一、Electron是什么
1、了解
Electron 是⼀个跨平台桌⾯应⽤开发框架,开发者可以使⽤:HTML、CSS、JavaScript 等 Web 技术来构建桌⾯应⽤程序,它的本质是结合了 Chromium 和 Node.js,现在⼴泛⽤于桌⾯应 ⽤程序开发。
2、技术架构

二、搭建一个demo
1、node.js和npm安装
出现版本号即安装。
js
node -v
npm -v
我本地开发node版本是14,需要切换成较高版本,推荐使用nvm切换,我切换成20可以跑,可以参考这篇nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)-CSDN博客
2、项目初始化
项目目录

2.1、创建文件+初始化文件
js
mkdir my-electron-app && cd my-electron-app
npm init
或者手动创建文件夹,之后进行初始化
2.2、安装electron
js
npm install --save-dev electron
//或者
npm install electron -D
2.3、配置自动更新
就不用每次保存后都npm start了
js
npm i nodemon -D
手动添加一个nodemon.json文件

js
{
"ignore": [ //忽略文件
"node_modules",
"dist"
],
"restartable": "r", //按r更新
"watch": ["*.*"],//监听所有文件
"ext": "js,html,css,json"//包含
}
2.4、得到package.json文件,添加start
js
{
"name": "electron-app",
"version": "1.0.0",
"main": "main.js",
"type": "pages",
"devDependencies": {
"electron": "^37.2.1",
"electron-builder": "^26.0.12",
"nodemon": "^3.1.10"
},
"scripts": {
"start": "nodemon --exec electron .",
"build": "electron-builder ",
"pack": "electron-builder --dir"
},
"build": {
"appId": "",
"productName": "测试electron-dome",
"directories": {
"output": "dist"
},
"files": [
"**/*",
"!node_modules/**/{README.md,CHANGELOG.md,test,*.ts}"
],
"win": {
"icon": "./assets/images/logo2.png",
"asar": true,
"requestedExecutionLevel": "",
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
]
}
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
}
}
2.5、创建main.js

js
console.log('Hello from Electron!')//在main.js中添加打印
之后运行
js
npm start
终端出现打印内容,搭建成功

2.6、内容安全策略
运行之后,控制台有黄色提示文字,解决方法在index.html里加入代码
js
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://*; child-src 'none';" />
<!-- self 同源 https://* 跨域 'none' 禁用所有子资源 'unsafe-inline' 允许内联样式 data: URI来嵌⼊图像。这种URI模式允许将图像数据直接嵌⼊到HTML或CSS中,⽽不是通过外部链接引⽤ -->
关于CSP的详细说明:内容安全策略详解
2.7、兼容mac和windows(适用性更好
mac关闭程序之后并没有直接退出应用,windows直接退出
为了兼容这点,在main.js添加代码段
js
console.log("Hello from Electron!");
const { app, BrowserWindow } = require("electron");
function createWindow() {
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
}
app.on("ready", () => {
createWindow();
const win = new BrowserWindow({
// https://www.electronjs.org/zh/docs/latest/api/browser-window 一些相关api
width: 800,
height: 600,
autoHideMenuBar: true, //隐藏菜单栏
x: 100, //窗口位置
y: 100, //窗口位置
resizable: false, //禁止改变窗口大小
webPreferences: {
nodeIntegration: true,
},
});
win.loadFile("./pages/index.html");
});
// 监听所有窗口关闭事件
app.on("window-all-closed", () => {
// 如果所处平台不是mac(darwin),则退出应⽤。
if (process.platform !== "darwin") app.quit();
});
// 监听mac下点击 dock 图标时触发
app.on("activate", () => {
//如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
3、制作程序
3.1、加载一个链接页面loadURL
在main.js写入
js
console.log("Hello from Electron!");
const { app, BrowserWindow } = require("electron");
app.on("ready", () => {
const win = new BrowserWindow({
// https://www.electronjs.org/zh/docs/latest/api/browser-window 一些相关api
width: 800,
height: 600,
autoHideMenuBar: true, //隐藏菜单栏
x: 100, //窗口位置
y: 100, //窗口位置
resizable: false, //禁止改变窗口大小
webPreferences: {
nodeIntegration: true,
},
});
//加载一个页面
win.loadURL('https://www.baidu.com/')
});
3.2、加载自己的页面loadFile
改为win.loadFile("./pages/index.html"); 同时新建一个index.html
正常写前端页面即可
3.3、实现主进程与渲染进程之间的通信(IPC) 模组(重点理解)
现在main.js与render.js相当于独立的两个模块,比如main.js中可以输出node中的变量process,比如:
js
console.log(process.version); //打印node的版本号
console.log(process.versions.chrome);// 打印chrome的版本号
console.log(process.versions.electron);// 打印electron的版本号
而render.js中可以获取window的内容。
想让两者之间产生通信,所以就有"中间商"preload.js(顾名思义,预加载,最好还是用这个名字),预加载脚本在渲染器加载网页之前注入,但它仍是在渲染进程中运行的脚本,可以访问到渲染进程的全局对象,也可以访问Node.js的API。
3.3.0 引入预加载脚本preload.js

在main.js中添加webPreferences属性,设置preload属性为preload.js的路径
js
const path = require("path");
....
preload: path.resolve(__dirname, "./preload.js"),//写绝对路径
main.js文件
js
// 一、导入必要的 Electron 模块
// 二、定义创建窗口的函数
// 三、当 Electron 初始化完成后创建窗口
// 四、在窗口中加载 HTML 界面
// app:控制应用程序生命周期的模块
// BrowserWindow:用于创建和控制浏览器窗口的模块
// ipcMain:用于处理主进程与渲染进程之间通信(IPC)的模块
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require("path");
const fs = require("fs"); // node API
// 1. 创建一个 800×600 像素的窗口
// 2. webPreferences.nodeIntegration: true 允许在渲染进程中使用 Node.js API
// 在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径
// __dirname 字符串指向当前正在执行的脚本的路径
// path.join API 将多个路径联结在一起,创建一个跨平台的路径字符串
// 3. win.loadFile('index.html') 加载当前目录下的 index.html 文件作为窗口内容
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
// preload: path.join(__dirname, 'preload.js'),
preload: path.resolve(__dirname, "preload.js"),
},
})
ipcMain.on("save-file", (event, data) => {
// 保存文件writeFileSync方法
fs.writeFileSync("E://hello.txt", data);
})
// 使用 ipcMain.handle 设置一个主进程处理程序
ipcMain.handle("read-file", () => {
// 读取文件readFileSync方法,需要转成字符串
return fs.readFileSync("E://hello.txt").toString();
});
// 加载本地的页面loadFile
win.loadFile('./pages/dome/index.html');
// 正确加载 HTML 文件
// if (app.isPackaged) {
// // 生产环境路径
// win.loadFile(path.join(__dirname, './pages/index.html'))
// } else {
// // 开发环境路径
// win.loadFile('./pages/index.html')
// }
if (!app.isPackaged) {
win.webContents.openDevTools();
}
// 在加载文件loadFile之后设置一个定时器
// setTimeout(() => {
// // 5秒后发送消息
// win.webContents.send("message", "测试"); // send对应on
// }, 5000);
// //加载一个页面
// win.loadURL('https://www.baidu.com/')
}
// 启动应用:
// app.whenReady() 等待 Electron 完成初始化
// 当准备就绪后,调用 createWindow 函数创建窗口
app.whenReady().then(() => {
createWindow();
// 监听mac下点击 dock 图标时触发
app.on('activate', () => {
//如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
});
// 当所有窗口关闭时退出应用(mac 除外)
app.on('window-all-closed', () => {
// 所处平台不是mac(darwin),则调用 app.quit() 来退出应用程序
if (process.platform !== 'darwin') {
app.quit();
}
})
3.3.1、主进程←渲染进程
demo1:点击按钮,可以在D盘创建一个hello.txt文件,文件内容可自己输入
1.先在页面创建按钮及输入框元素,在renderjs中添加事件
index.html
js
<input id="input1"></input>
<button id="btn2">现在我要往D盘hello.txt写入内容</button>
render.js
js
// 渲染进程
const btn2 = document.getElementById('btn2');
const input1 = document.getElementById('input1');
btn2.onclick = () => {
myData.saveFile(input1.value);
};
预加载脚本preload.js
js
const { contextBridge, ipcRenderer } = require("electron");//引入"桥梁"
contextBridge.exposeInMainWorld("myData", {
version: process.version, // 暴露process.versions对象,包含node和chrome版本号
saveFile: (data) => { // 暴露saveFile方法,用于保存文件
ipcRenderer.send("save-file", data); // 发送save-file事件,携带data参数
}
})
主进程main.js
注意在加载文件loadFile之前要订阅消息
js
const { app, BrowserWindow, ipcMain } = require("electron");
const fs = require("fs");//node API
...
ipcMain.on("save-file", (event, data) => {
// 保存文件writeFileSync方法
fs.writeFileSync("D://hello.txt", data);
});
win.loadFile("./pages/index.html");
这样就实现了

3.3.2、主进程→(双向)←渲染进程
demo2:点击按钮,可以在D盘创建一个hello.txt文件,文件内容可自己输入,同时可以实现读取(点击按钮,可以将文件内容写入文本框
1.先在页面创建按钮及输入框元素,在renderjs中添加事件
index.html
js
<input id="input1"></input>
<button id="btn2">现在我要往D盘hello.txt写入内容</button>
<button id="btn3">读取hello.txt内容</button>
render.js
js
// 渲染进程
const btn2 = document.getElementById('btn2');
const input1 = document.getElementById('input1');
const btn3 = document.getElementById('btn3');
btn2.onclick = () => {
myData.saveFile(input1.value);
};
btn3.onclick = () => {
// 读取内容,然后写入到input1中
myData.readFile().then((data) => {
input1.value = data;
});
};
预加载脚本preload.js
js
const { contextBridge, ipcRenderer } = require("electron");//引入"桥梁"
contextBridge.exposeInMainWorld("myData", {
version: process.version, // 暴露process.versions对象,包含node和chrome版本号
saveFile: (data) => { // 暴露saveFile方法,用于保存文件
ipcRenderer.send("save-file", data); // 发送save-file事件,携带data参数
},
readFile(){ // 暴露readFile方法,用于读取文件
return ipcRenderer.invoke("read-file"); // 发送read-file事件,不携带参数
}
})
主进程main.js
注意在加载文件loadFile之前要订阅消息
js
const { app, BrowserWindow, ipcMain } = require("electron");
const fs = require("fs");//node API
...
ipcMain.on("save-file", (event, data) => { //send对应on
// 保存文件writeFileSync方法
fs.writeFileSync("D://hello.txt", data);
});
ipcMain.handle("read-file", () => { //invoke对应handle,返回值是一个promise
// 保存文件
return fs.readFileSync("D://hello.txt").toString();//需要转成字符串
});
win.loadFile("./pages/index.html");
这样就实现了

3.3.3、主进程→渲染进程
demo3:应用加载6s后,让渲染进程弹出"测试" 主进程main.js
在加载文件loadFile之后设置一个定时器
js
...
win.loadFile("./pages/index.html");
setTimeout(() => {
// 发送消息
win.webContents.send("message", "测试"); //send对应on
}, 6000);
预加载脚本preload.js
js
const { contextBridge, ipcRenderer } = require("electron");//引入"桥梁"
contextBridge.exposeInMainWorld("myData", {
...
getMessage: (callback) => { // 暴露getMessage方法,用于获取消息
return ipcRenderer.on("message", callback);
}
})
render.js
js
// 渲染进程
const btn2 = document.getElementById('btn2');
const input1 = document.getElementById('input1');
const btn3 = document.getElementById('btn3');
btn2.onclick = () => {
myData.saveFile(input1.value);
};
btn3.onclick = () => {
// 读取内容,然后写入到input1中
myData.readFile().then((data) => {
input1.value = data;
});
};
//新增
window.onload = () => {
myData.getMessage((event, str) => { // 接收message事件,携带data参数
alert(str); // 弹出data
});
};
这样就实现了

4、打包程序
4.1 electron-vite
官网地址:electron-vite | 下一代 Electron 开发构建工具
js
npm i electron-vite -D
electron-vite 是 Vue + Vite 项目的最佳选择,兼顾开发效率与现代化工具链。
4.2 electron-builder
js
npm install electron-builder -D

针对 Vue 项目的打包工具推荐
场景分析
- 你的项目基于 Vue,可能已使用 Vite 或 Webpack 构建。若希望保留现有工具链,优先选择与 Vite 集成的方案。
- 若项目需深度系统集成(如硬件调用),需权衡性能与开发效率。
推荐方案
1. 优先选择 electron-vite
理由:
- 天然适配 Vite 项目,支持 Vue 3 的热更新和按需编译,开发体验流畅。
- 统一配置简化多进程管理,适合中小型项目快速迁移到 Electron。
适用场景:Vue 3 + Vite 技术栈,需快速迭代和轻量打包。
2. 复杂项目选 electron-builder
理由:
- 支持多平台安装包生成(如 NSIS、DMG),适合企业级分发需求。
- 可通过 Webpack 优化依赖,灵活控制打包体积。
适用场景:需定制安装流程、自动更新或签名机制的大型项目。