1. 为什么选择Electron Vite Vue3组合如果你正在寻找一个现代、高效且易于维护的桌面应用开发方案Electron Vite Vue3的组合绝对值得考虑。这个技术栈结合了三个领域的佼佼者Electron提供了跨平台桌面应用的能力Vite带来了极速的开发体验而Vue3则让前端开发变得更加优雅和高效。我在多个企业级项目中实际使用过这个组合发现它特别适合需要快速迭代但又要求稳定性的商业应用。比如最近开发的一个金融数据分析工具从零开始搭建到第一个可演示版本只用了两周时间这在传统桌面应用开发中几乎是不可能完成的任务。这个组合最大的优势在于开发效率。Vite的热更新速度极快修改代码后几乎瞬间就能看到变化这大大减少了等待时间。而Vue3的Composition API让代码组织更加灵活特别是对于复杂的状态管理场景。Electron则让这一切都能以桌面应用的形式呈现可以直接访问系统资源这是纯Web应用无法比拟的。2. 项目初始化与环境搭建2.1 创建基础项目结构首先我们需要创建一个基础项目。我推荐使用electron-vite-boilerplate这个模板它已经为我们配置好了Electron、Vite和Vue3的基本集成。npx degit alex8088/electron-vite-boilerplate your-project-name cd your-project-name npm install npm run dev执行完这些命令后你会看到一个基本的Electron窗口弹出里面运行着Vue3应用。这个模板已经帮我们处理了主进程和渲染进程的通信问题省去了很多配置时间。2.2 项目结构解析让我们看看生成的项目结构your-project-name/ ├── build/ # 打包相关配置 ├── resources/ # 静态资源 ├── src/ │ ├── main/ # Electron主进程代码 │ ├── preload/ # 预加载脚本 │ └── renderer/ # Vue3渲染进程代码 ├── electron.vite.config.ts # 主配置文件 └── package.json在实际开发中我们主要关注的是renderer目录这里存放着我们的Vue3应用代码。除非必要一般不需要修改main和preload目录的内容。3. 核心功能模块配置3.1 路由系统搭建对于企业级应用路由管理是必不可少的。我们先安装vue-routernpm install vue-router4然后在src/renderer/src目录下创建router/index.ts文件import { createRouter, createWebHashHistory } from vue-router import type { RouteRecordRaw } from vue-router const routes: RouteRecordRaw[] [ { path: /, name: Home, component: () import(/views/Home.vue) }, { path: /about, name: About, component: () import(/views/About.vue) } ] const router createRouter({ history: createWebHashHistory(), routes }) export default router在main.ts中注册路由import { createApp } from vue import App from ./App.vue import router from ./router const app createApp(App) app.use(router) app.mount(#app)3.2 状态管理方案对于复杂的企业应用我们需要一个强大的状态管理方案。Pinia是Vue3官方推荐的状态管理库比Vuex更简单、更符合Vue3的设计理念。安装Pinia和持久化插件npm install pinia pinia-plugin-persistedstate创建store目录结构store/ ├── modules/ # 各业务模块的store │ ├── user.ts │ └── app.ts └── index.ts # 主入口文件在main.ts中初始化Piniaimport { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstate const pinia createPinia() pinia.use(piniaPluginPersistedstate) app.use(pinia)创建一个用户模块的store示例// store/modules/user.ts import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: , userInfo: null }), actions: { setToken(token: string) { this.token token }, async fetchUserInfo() { // 获取用户信息的逻辑 } }, persist: { enabled: true, strategies: [ { key: user, storage: localStorage } ] } })4. 企业级功能增强4.1 API请求封装企业级应用通常需要与后端API进行大量交互。我们可以封装一个统一的请求工具// utils/request.ts import axios from axios import { useUserStore } from /store/modules/user const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) service.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) service.interceptors.response.use( response { return response.data }, error { if (error.response?.status 401) { // 处理未授权情况 } return Promise.reject(error) } ) export default service然后我们可以按模块组织API// api/user.ts import request from /utils/request export function login(data: { username: string; password: string }) { return request.post(/auth/login, data) } export function getUserInfo() { return request.get(/user/info) }4.2 全局错误处理在企业级应用中统一的错误处理机制非常重要。我们可以扩展之前的请求封装// utils/request.ts service.interceptors.response.use( response { const { code, message, data } response.data if (code ! 0) { // 显示错误提示 return Promise.reject(new Error(message)) } return data }, error { let errorMessage 网络错误 if (error.response) { switch (error.response.status) { case 400: errorMessage 请求参数错误 break case 401: errorMessage 未授权请重新登录 // 跳转到登录页 break case 500: errorMessage 服务器内部错误 break } } // 可以在这里集成错误上报 return Promise.reject(new Error(errorMessage)) } )5. 性能优化与生产环境配置5.1 打包优化Vite默认的打包配置已经很优秀但我们还可以进一步优化。修改electron.vite.config.tsimport { defineConfig } from electron-vite import { resolve } from path import { visualizer } from rollup-plugin-visualizer export default defineConfig({ main: { build: { rollupOptions: { output: { manualChunks: (id) { if (id.includes(node_modules)) { return vendor } } } } } }, renderer: { build: { rollupOptions: { plugins: [ visualizer({ open: true, gzipSize: true, brotliSize: true }) ] } } } })5.2 多环境配置企业级应用通常需要区分开发、测试和生产环境。我们可以使用.env文件# .env.development VITE_API_BASE_URLhttp://localhost:3000/api VITE_APP_ENVdevelopment# .env.production VITE_API_BASE_URLhttps://api.yourdomain.com VITE_APP_ENVproduction在代码中可以通过import.meta.env访问这些变量const baseURL import.meta.env.VITE_API_BASE_URL5.3 主进程优化Electron的主进程是单线程的长时间运行的任务会阻塞UI。我们可以使用worker线程来处理耗时操作// src/main/worker.ts import { Worker, isMainThread, parentPort } from worker_threads if (!isMainThread) { parentPort?.on(message, (task) { // 处理耗时任务 const result heavyTask(task) parentPort?.postMessage(result) }) } export function runInWorker(task: any) { return new Promise((resolve) { const worker new Worker(__filename) worker.on(message, resolve) worker.postMessage(task) }) }6. 常见问题与解决方案6.1 跨进程通信优化Electron的主进程和渲染进程之间的通信可能会成为性能瓶颈。我们可以优化通信方式// src/preload/index.ts import { contextBridge, ipcRenderer } from electron contextBridge.exposeInMainWorld(electronAPI, { send: (channel: string, data: any) { ipcRenderer.send(channel, data) }, on: (channel: string, func: (...args: any[]) void) { ipcRenderer.on(channel, (event, ...args) func(...args)) }, invoke: (channel: string, ...args: any[]) { return ipcRenderer.invoke(channel, ...args) } })在渲染进程中// 调用主进程方法 window.electronAPI.invoke(some-action, data).then(result { // 处理结果 }) // 监听主进程事件 window.electronAPI.on(some-event, (data) { // 处理事件 })6.2 内存泄漏排查Electron应用容易出现内存泄漏问题。我们可以使用Chrome DevTools来排查在开发模式下运行应用打开Chrome DevToolsCtrlShiftI切换到Memory面板进行一系列操作后拍摄堆快照比较多次快照查找不断增长的对象6.3 原生模块兼容性如果需要使用Node.js原生模块可能会遇到兼容性问题。解决方案确保模块支持Electron的Node.js版本使用electron-rebuild重新编译模块npm install --save-dev electron-rebuild npx electron-rebuild如果模块不支持Electron考虑寻找替代方案或封装为独立服务7. 项目实战构建一个完整的桌面应用7.1 应用架构设计让我们设计一个典型的桌面应用架构src/ ├── main/ # Electron主进程 ├── preload/ # 预加载脚本 └── renderer/ # Vue3渲染进程 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── composables/# 组合式函数 ├── router/ # 路由配置 ├── store/ # 状态管理 ├── styles/ # 全局样式 ├── utils/ # 工具函数 ├── views/ # 页面组件 ├── App.vue # 根组件 └── main.ts # 入口文件7.2 主题切换实现企业应用通常需要支持主题切换。我们可以使用CSS变量和Pinia来实现// store/modules/app.ts export const useAppStore defineStore(app, { state: () ({ theme: light }), actions: { toggleTheme() { this.theme this.theme light ? dark : light document.documentElement.setAttribute(data-theme, this.theme) } } })在全局CSS中定义主题变量:root { --primary-color: #409eff; --bg-color: #ffffff; --text-color: #333333; } [data-themedark] { --primary-color: #64b5f6; --bg-color: #1e1e1e; --text-color: #f5f5f5; }7.3 多窗口管理复杂的桌面应用可能需要管理多个窗口。我们可以在主进程中实现窗口管理器// src/main/windowManager.ts import { BrowserWindow, app } from electron import path from path const windows new Mapstring, BrowserWindow() export function createWindow(id: string, options {}) { if (windows.has(id)) { const win windows.get(id) win?.focus() return win } const win new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, ../preload/index.js), nodeIntegration: false, contextIsolation: true }, ...options }) if (import.meta.env.DEV) { win.loadURL(http://localhost:3000) win.webContents.openDevTools() } else { win.loadFile(path.join(__dirname, ../renderer/index.html)) } windows.set(id, win) win.on(closed, () windows.delete(id)) return win }7.4 系统托盘集成桌面应用通常需要系统托盘功能// src/main/tray.ts import { Tray, Menu, app } from electron import path from path let tray: Tray | null null export function createTray() { const iconPath path.join(__dirname, ../../resources/tray.png) tray new Tray(iconPath) const contextMenu Menu.buildFromTemplate([ { label: 打开主窗口, click: () { // 打开主窗口逻辑 } }, { label: 退出, click: () { app.quit() } } ]) tray.setToolTip(我的应用) tray.setContextMenu(contextMenu) }