Electron+Vue3桌面应用实战:从零封装带Pinia的系统托盘应用
引言
最近在开发一个桌面端效率工具时,遇到了一个有趣的场景:需要让应用在关闭窗口后仍能驻留在系统托盘,同时支持通过右键菜单快速唤醒功能面板。这让我意识到,系统托盘作为Electron的特色功能之一,在实际项目中有着广泛的应用价值。
本文将带您从零开始,用Electron+Vue3+TypeScript+Pinia构建一个完整的系统托盘应用。不同于简单的Demo,我们会重点解决几个工程实践中的关键问题:
- 如何优雅地封装系统托盘模块,使其支持动态菜单和状态管理
- 如何实现主进程与渲染进程之间的高效通信
- 如何利用Pinia在多个窗口间同步应用状态
- 如何处理Electron特有的生命周期问题
我们将以一个便签应用为例,但核心架构可以复用到任何需要系统托盘功能的场景。最终效果是:应用关闭窗口后驻留托盘,右键菜单可新建便签、切换主题等,所有窗口共享同一状态管理。
1. 环境准备与项目初始化
1.1 创建基础项目
首先确保系统已安装Node.js 18+,然后通过以下命令创建项目:
pnpm create @quick-start/electron my-tray-app --template vue-ts
选择vue-ts模板会帮我们自动集成好Electron+Vue3+TypeScript的基础配置。创建完成后,目录结构如下:
my-tray-app/
├── src/
│ ├── main/ # 主进程代码
│ ├── preload/ # 预加载脚本
│ └── renderer/ # 渲染进程(Vue)
├── electron.vite.config.ts
└── package.json
1.2 安装必要依赖
我们需要额外添加几个关键依赖:
pnpm add pinia @pinia/npm-plugin electron-log
pnpm add -D @types/node
pinia: Vue3推荐的状态管理库electron-log: 主进程日志工具@types/node: Node.js类型定义
1.3 配置Pinia
在src/renderer/main.ts中初始化Pinia:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
2. 系统托盘核心模块封装
2.1 创建TrayService类
在src/main/services目录下新建TrayService.ts:
import { Tray, Menu, nativeImage } from 'electron'
import path from 'path'
import { IpcMainInvokeEvent } from 'electron/main'
export class TrayService {
private tray: Tray | null = null
private static instance: TrayService
private constructor() {}
public static getInstance(): TrayService {
if (!TrayService.instance) {
TrayService.instance = new TrayService()
}
return TrayService.instance
}
public init(iconPath: string) {
const icon = nativeImage.createFromPath(
path.join(__dirname, iconPath)
)
this.tray = new Tray(icon.resize({ width: 16, height: 16 }))
this.setContextMenu([
{ label: '新建便签', click: () => this.createNote() },
{ type: 'separator' },
{ label: '退出', role: 'quit' }
])
this.tray.setToolTip('我的便签应用')
}
private createNote() {
// 与渲染进程通信创建新窗口
}
public setContextMenu(menuItems: Electron.MenuItemConstructorOptions[]) {
if (!this.tray) return
const menu = Menu.buildFromTemplate(menuItems)
this.tray.setContextMenu(menu)
}
}
2.2 在主进程中使用TrayService
修改src/main/index.ts:
import { app } from 'electron'
import { TrayService } from './services/TrayService'
app.whenReady().then(() => {
const trayService = TrayService.getInstance()
trayService.init('../../resources/icon.png')
// 其他初始化代码...
})
3. 状态管理与进程通信
3.1 创建Pinia Store
在src/renderer/stores下创建appStore.ts:
import { defineStore } from 'pinia'
interface AppState {
theme: 'light' | 'dark'
notes: Note[]
}
interface Note {
id: string
content:


1415

被折叠的 条评论
为什么被折叠?



