Electron 现代开发最佳实践:安全通信、Vue3 集成与托盘应用(附完整代码)
·
一、核心前置:现代 Electron 安全原则
Electron 团队在新版本中强烈建议:
- 禁用
nodeIntegration:避免渲染进程直接访问 Node.js API,降低安全风险; - 开启
contextIsolation:隔离主进程和渲染进程的上下文,防止原型链污染; - 使用预加载脚本(preload):作为主 / 渲染进程的安全通信桥梁,仅暴露必要的 API;
- 限制
webSecurity:仅在开发阶段临时关闭,生产环境必须开启。
这是本文案例的核心配置准则,区别于入门级的 “全开放” 写法。
二、环境准备
1. 初始化项目(Vite + Vue3 + Electron)
我们使用 electron-vite 快速搭建集成 Vue3 的项目(无需手动配置 Vite 和 Electron 联动):
# 创建项目
npm create electron-vite@latest electron-vue3-demo -- --template vue
# 进入项目目录
cd electron-vue3-demo
# 安装依赖
npm install
项目结构说明(关键文件):
electron-vue3-demo/
├── electron/ # Electron 主进程目录
│ ├── main/ # 主进程代码
│ │ └── index.js # 主进程入口
│ └── preload/ # 预加载脚本目录
│ └── index.js # 预加载脚本入口
├── src/ # Vue3 渲染进程代码
│ ├── App.vue # 根组件
│ └── main.js # Vue 入口
├── index.html # 渲染进程入口 HTML
└── package.json # 项目配置
2. 核心依赖版本
本文案例基于以下版本(兼容性最佳):
- Electron:^29.0.0
- Vue:^3.3.0
- Vite:^5.0.0
- electron-vite:^2.0.0
三、实战 1:搭建安全的主进程与预加载脚本
1. 主进程配置(electron/main/index.js)
核心实现窗口创建、托盘初始化、IPC 监听,全程遵循安全原则:
const { app, BrowserWindow, ipcMain, Tray, Menu, Notification } = require('electron');
const path = require('node:path');
const os = require('node:os');
// 全局变量:主窗口、托盘实例
let mainWindow = null;
let tray = null;
// 创建主窗口
const createWindow = () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
// 禁用窗口缩放(可选)
resizable: true,
// 安全配置核心
webPreferences: {
// 开启上下文隔离(必须)
contextIsolation: true,
// 禁用 Node.js 直接集成(必须)
nodeIntegration: false,
// 预加载脚本:唯一的通信桥梁
preload: path.join(__dirname, '../preload/index.js'),
// 生产环境开启 webSecurity
webSecurity: process.env.NODE_ENV !== 'development',
// 禁用远程模块(Electron 14+ 已移除,此处仅作提醒)
enableRemoteModule: false
}
});
// 加载页面:开发环境加载 Vite 开发服务器,生产环境加载打包后的 HTML
if (process.env.VITE_DEV_SERVER_URL) {
mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
// 开发环境打开开发者工具
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../../dist/index.html'));
}
// 窗口最小化到托盘(而非关闭)
mainWindow.on('minimize', (e) => {
e.preventDefault(); // 阻止默认关闭行为
mainWindow.hide(); // 隐藏窗口
});
// 关闭所有窗口时退出应用(macOS 除外)
mainWindow.on('closed', () => {
mainWindow = null;
});
};
// 创建系统托盘
const createTray = () => {
// 托盘图标(这里使用 Electron 默认图标,实际项目需替换为自有图标)
const iconPath = path.join(
__dirname,
os.platform() === 'win32' ? 'tray.ico' : 'tray.png'
);
// 实例化托盘
tray = new Tray(iconPath);
// 设置托盘提示文本
tray.setToolTip('Electron Vue3 托盘应用');
// 构建托盘右键菜单
const trayMenu = Menu.buildFromTemplate([
{
label: '显示窗口',
click: () => mainWindow.show()
},
{
label: '发送通知',
click: () => sendNotification()
},
{
type: 'separator' // 分隔线
},
{
label: '退出',
click: () => {
// 退出前销毁托盘,防止进程残留
tray.destroy();
app.quit();
}
}
]);
// 设置托盘菜单
tray.setContextMenu(trayMenu);
// 点击托盘图标显示/隐藏窗口
tray.on('click', () => {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
});
};
// 发送系统通知
const sendNotification = () => {
// 检查系统是否支持通知
if (Notification.isSupported()) {
const notification = new Notification({
title: 'Electron 通知',
body: '这是一条安全的系统通知 🚀',
// 可选:通知图标
icon: path.join(__dirname, 'notification.png')
});
// 显示通知
notification.show();
// 通知点击事件
notification.on('click', () => {
mainWindow.show();
});
}
};
// IPC 通信:监听渲染进程的“获取系统信息”请求
ipcMain.handle('get-system-info', () => {
return {
platform: os.platform(), // 系统平台(win32/darwin/linux)
arch: os.arch(), // 系统架构
homedir: os.homedir() // 用户主目录
};
});
// IPC 通信:监听渲染进程的“发送通知”请求
ipcMain.on('send-notification', (_, msg) => {
const notification = new Notification({
title: '来自渲染进程的通知',
body: msg
});
notification.show();
});
// Electron 应用就绪后初始化
app.whenReady().then(() => {
createWindow();
createTray(); // 初始化托盘
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 处理跨平台退出逻辑
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// 禁止应用多开(可选)
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
});
}
2. 预加载脚本(electron/preload/index.js)
预加载脚本运行在独立的上下文,是主 / 渲染进程通信的唯一安全桥梁,仅暴露必要 API:
const { contextBridge, ipcRenderer } = require('electron');
// 向渲染进程暴露安全的 API 接口
// 注意:仅暴露需要的方法,避免暴露整个 ipcRenderer
contextBridge.exposeInMainWorld('electronAPI', {
// 获取系统信息(使用 invoke 调用主进程的 handle 监听)
getSystemInfo: () => ipcRenderer.invoke('get-system-info'),
// 发送自定义通知(使用 send 调用主进程的 on 监听)
sendNotification: (msg) => ipcRenderer.send('send-notification', msg),
// 窗口最小化(直接调用渲染进程的 webContents 方法)
minimizeWindow: () => ipcRenderer.send('window-minimize'),
// 窗口关闭
closeWindow: () => ipcRenderer.send('window-close')
});
// 可选:监听主进程主动发送的消息
ipcRenderer.on('main-message', (_, data) => {
console.log('主进程发送的消息:', data);
});
// 补充:主进程监听窗口最小化/关闭(需在 main.js 中添加)
ipcRenderer.on('window-minimize', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) win.minimize();
});
ipcRenderer.on('window-close', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) win.close();
});
四、实战 2:Vue3 渲染进程开发
1. Vue 根组件(src/App.vue)
集成 Electron 原生能力,通过预加载脚本暴露的 electronAPI 实现通信:
<template>
<div class="container">
<header class="header">
<h1>Electron + Vue3 现代开发示例</h1>
<div class="window-controls">
<button @click="minimizeWindow">🗕</button>
<button @click="closeWindow">🗙</button>
</div>
</header>
<main class="content">
<!-- 系统信息展示 -->
<div class="system-info">
<h2>系统信息</h2>
<button @click="getSystemInfo" class="btn">获取系统信息</button>
<pre v-if="systemInfo">{{ JSON.stringify(systemInfo, null, 2) }}</pre>
</div>
<!-- 自定义通知 -->
<div class="notification">
<h2>发送系统通知</h2>
<input
v-model="notificationMsg"
type="text"
placeholder="输入通知内容..."
class="input"
/>
<button @click="sendNotification" class="btn">发送通知</button>
</div>
</main>
</div>
</template>
<script setup>
import { ref } from 'vue';
// 响应式数据
const systemInfo = ref(null);
const notificationMsg = ref('Hello Electron + Vue3!');
// 从预加载脚本暴露的 API 中获取方法
const { getSystemInfo, sendNotification, minimizeWindow, closeWindow } = window.electronAPI;
// 获取系统信息
const getSystemInfo = async () => {
try {
const info = await getSystemInfo();
systemInfo.value = info;
} catch (err) {
console.error('获取系统信息失败:', err);
alert('获取系统信息失败,请查看控制台');
}
};
// 发送自定义通知
const sendNotification = () => {
if (!notificationMsg.value) {
alert('请输入通知内容');
return;
}
sendNotification(notificationMsg.value);
};
</script>
<style scoped>
.container {
width: 100%;
height: 100vh;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px;
background-color: #2c3e50;
color: white;
-webkit-app-region: drag; /* 允许窗口拖拽(仅 macOS/Windows) */
}
.window-controls {
-webkit-app-region: no-drag; /* 取消拖拽,允许点击 */
}
.window-controls button {
background: transparent;
border: none;
color: white;
font-size: 16px;
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 4px;
}
.window-controls button:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.content {
flex: 1;
padding: 30px;
display: flex;
gap: 40px;
}
.system-info, .notification {
flex: 1;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.btn {
padding: 8px 16px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 10px;
}
.btn:hover {
background-color: #2980b9;
}
.input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
pre {
background-color: #f8f8f8;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
</style>
2. Vue 入口文件(src/main.js)
标准 Vue3 入口配置:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
3. 渲染进程入口 HTML(index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Electron + Vue3 应用</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
五、运行与调试
1. 开发环境运行
npm run dev
此时会启动 Vite 开发服务器和 Electron 应用,具备以下特性:
- Vue3 热更新(修改代码无需重启 Electron);
- 开发者工具自动打开;
- 窗口可拖拽(header 区域);
- 点击 “获取系统信息” 可展示当前系统的平台、架构、主目录;
- 输入文本后点击 “发送通知” 可触发系统原生通知;
- 窗口最小化会隐藏到托盘,点击托盘图标可恢复,右键托盘可操作。
2. 关键功能验证
- 安全通信:渲染进程无法直接访问
fs/os等 Node.js 模块,仅能通过预加载脚本暴露的 API 交互; - 托盘功能:窗口最小化后常驻系统托盘,右键菜单可操作;
- 系统通知:支持自定义通知内容,点击通知可唤醒窗口;
- 单实例运行:禁止应用多开,重复启动会聚焦已有窗口。
六、生产环境打包与优化
1. 打包命令
# 构建 Vue 代码并打包 Electron 应用
npm run build
打包完成后,输出文件位于 dist-electron 目录,包含对应平台的可执行文件(Windows:.exe、macOS:.dmg、Linux:.deb)。
2. 打包优化技巧
(1)减小应用体积
- 在
package.json中配置build.asar:将应用资源打包为 asar 归档,减少文件数量并防止资源篡改;"build": { "asar": true, "asarUnpack": ["**/*.node"] // 排除原生模块(如有) } - 使用
electron-builder的fileAssociations仅打包必要资源; - 移除开发依赖:打包前执行
npm prune --production。
(2)自定义打包图标
在 package.json 的 build 字段中配置图标:
"build": {
"icon": "resources/icon.ico", // Windows ico 格式,macOS 需 icns 格式
"mac": {
"icon": "resources/icon.icns"
}
}
(3)自动更新(可选)
集成 electron-updater 实现应用自动更新:
npm install electron-updater --save
在主进程中添加更新逻辑:
const { autoUpdater } = require('electron-updater');
// 检查更新
autoUpdater.checkForUpdatesAndNotify();
// 监听更新事件
autoUpdater.on('update-available', () => {
new Notification({
title: '发现新版本',
body: '正在下载更新,请稍候...'
}).show();
});
autoUpdater.on('update-downloaded', () => {
new Notification({
title: '更新完成',
body: '重启应用以应用更新'
}).show();
// 重启并安装更新
autoUpdater.quitAndInstall();
});
七、核心知识点总结
-
安全通信模型:
- 禁用
nodeIntegration+ 开启contextIsolation,杜绝渲染进程直接访问 Node.js API; - 预加载脚本通过
contextBridge仅暴露必要 API,实现 “最小权限” 原则; - IPC 通信使用
ipcMain.handle/ipcRenderer.invoke(双向通信)和ipcMain.on/ipcRenderer.send(单向通信)。
- 禁用
-
Vue3 集成:
- 使用
electron-vite快速搭建 Vite + Vue3 + Electron 项目,支持热更新; - 渲染进程通过
window.electronAPI调用原生能力,与 Vue 响应式体系无缝结合。
- 使用
-
原生能力扩展:
- 托盘(Tray):实现应用常驻系统托盘,支持右键菜单和点击事件;
- 系统通知(Notification):调用系统原生通知,支持点击唤醒窗口;
- 单实例锁:防止应用多开,提升用户体验。
-
打包优化:
- asar 归档减小体积并提升安全性;
- 自定义图标和打包配置,适配不同平台;
- 集成自动更新,简化应用维护。
八、扩展方向
- 集成 Element Plus/Naive UI 等 Vue 组件库,提升 UI 体验;
- 实现本地数据持久化(如
electron-store替代 localStorage); - 开发自定义协议(如
myapp://),实现外部链接唤醒应用; - 集成 WebSocket/HTTP 服务,实现跨设备通信;
- 适配 macOS 全屏、Windows 任务栏进度条等原生特性。
本文案例代码可直接运行,覆盖了现代 Electron 开发的核心安全规范和高频原生能力,是从 “入门” 到 “生产” 的关键过渡。遵循这些最佳实践,既能保证应用安全性,又能充分发挥 Electron 跨平台的优势。
更多推荐



所有评论(0)