Electron+Vue3桌面应用实战:从零封装一个带Pinia状态管理的系统托盘应用

Electron+Vue3桌面应用实战:从零封装带Pinia的系统托盘应用

引言

最近在开发一个桌面端效率工具时,遇到了一个有趣的场景:需要让应用在关闭窗口后仍能驻留在系统托盘,同时支持通过右键菜单快速唤醒功能面板。这让我意识到,系统托盘作为Electron的特色功能之一,在实际项目中有着广泛的应用价值。

本文将带您从零开始,用Electron+Vue3+TypeScript+Pinia构建一个完整的系统托盘应用。不同于简单的Demo,我们会重点解决几个工程实践中的关键问题:

  1. 如何优雅地封装系统托盘模块,使其支持动态菜单和状态管理
  2. 如何实现主进程与渲染进程之间的高效通信
  3. 如何利用Pinia在多个窗口间同步应用状态
  4. 如何处理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: 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值