Electron + Vue3 桌面应用开发实战指南

Electron + Vue3 桌面应用开发实战指南

作为一名拥有 8 年前端开发经验的工程师,我深知从 Web 到桌面应用开发的转型痛点。Electron 框架虽然降低了跨平台开发门槛,但实际项目中仍会遇到各种棘手问题。本文基于最新环境(Node.js v20.12.2、npm v9.8.1、Electron v29.1.1),结合实战经验,从基础搭建到生产部署,全方位讲解 Electron+Vue3 开发要点。

一、Electron 原生开发核心流程

1. 项目初始化最佳实践

csharp 复制代码
npm init -y

关键注意点

  • package.json中的name字段必须是小写字母 + 连字符组合(如my-electron-app),避免中文或特殊字符,否则后续打包可能出现莫名其妙的错误
  • 初始化后立即设置"private": true,防止意外发布到 npm 仓库

2. Electron 安装与镜像配置

国内网络环境下,直接安装 Electron 极易失败,推荐以下配置方案:

ini 复制代码
# 创建.npmrc文件(优先级高于全局配置)
cat > .npmrc << EOF
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
registry=https://registry.npmmirror.com/
EOF
# 安装指定版本(锁定版本号是项目稳定的关键)
npm install electron@29.1.1 --save-dev

验证安装

bash 复制代码
npx electron --version  # 成功输出版本号即安装正常

3. 核心文件编写规范

index.html(渲染进程)
xml 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <!-- 内容安全策略(开发环境配置) -->
  <meta http-equiv="Content-Security-Policy" 
        content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
  <title>Electron基础示例</title>
</head>
<body>
  <h1>Hello Electron 👋</h1>
  <p>当前版本: <span id="version"></span></p>
  <script>
    // 通过预加载脚本暴露的API获取版本信息
    document.getElementById('version').textContent = window.electronAPI.getVersion()
  </script>
</body>
</html>
preload.js(进程通信桥梁)
javascript 复制代码
// 严格的进程间通信边界是安全的关键
const { contextBridge } = require('electron/renderer')
// 只暴露必要的API,禁止直接暴露整个process对象
contextBridge.exposeInMainWorld('electronAPI', {
  getVersion: () => process.versions.electron,
  // 后续可添加更多通信方法
  // showDialog: (message) => ipcRenderer.invoke('show-dialog', message)
})
main.js(主进程)
less 复制代码
const { app, BrowserWindow } = require('electron/main')
const { join } = require('path')
let mainWindow = null
const createWindow = () => {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // 预加载脚本路径
      preload: join(__dirname, 'preload.js'),
      // 安全配置
      contextIsolation: true,    // 必须开启上下文隔离
      nodeIntegration: false,    // 禁止渲染进程直接访问Node.js
      enableRemoteModule: false, // 已废弃的API,确保关闭
      sandbox: false             // 生产环境可考虑开启沙箱
    }
  })
  // 加载页面
  mainWindow.loadFile(join(__dirname, 'index.html'))
  // 开发环境自动打开调试工具
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools({ mode: 'detach' })
  }
  // 窗口关闭时清理资源
  mainWindow.on('closed', () => {
    mainWindow = null
  })
}
// 应用生命周期管理
app.whenReady().then(() => {
  createWindow()
  // macOS特殊处理
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})
// 所有窗口关闭时退出应用(macOS除外)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

4. 开发命令配置

json 复制代码
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development electron .",
    "start": "npm run dev"
  },
  "devDependencies": {
    "cross-env": "^7.0.3",  // 跨平台设置环境变量
    "electron": "^29.1.1"
  }
}

安装 cross-env:npm install cross-env --save-dev

二、Vue3 + Electron 高效开发方案

对于中大型项目,推荐使用 electron-vite 框架,它完美整合了 Vite 的热更新能力和 Electron 的桌面特性。

1. 项目创建与配置

sql 复制代码
# 推荐使用pnpm(速度更快,依赖管理更严格)
npm install -g pnpm
pnpm create @quick-start/electron@latest

交互选项建议

  • Project name: 用小写英文 + 连字符(如vue-electron-app)
  • Select a framework: vue
  • Add TypeScript? Yes(长期项目强烈建议使用 TS)
  • Add Electron updater plugin? Yes(提前集成自动更新)
  • Enable Electron download mirror proxy? Yes(国内加速)

2. 项目结构深度解析

bash 复制代码
vue-electron-app/
├── electron/               # 主进程代码(Node环境)
│   ├── main/               # 主进程核心逻辑
│   │   └── index.js        # 主进程入口
│   └── preload/            # 预加载脚本
│       └── index.js        # 预加载入口
├── src/                    # 渲染进程代码(Vue环境)
│   ├── components/         # Vue组件
│   ├── App.vue             # 根组件
│   └── main.js             # Vue入口
├── electron.vite.config.js # 构建配置(核心文件)
└── package.json            # 项目配置

3. 热更新优化配置

electron-vite 已内置热更新,通过electron.vite.config.js可进一步优化:

php 复制代码
import { defineConfig } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
import { join } from 'path'
export default defineConfig({
  main: {
    build: {
      outDir: 'dist-electron/main'
    }
  },
  preload: {
    build: {
      outDir: 'dist-electron/preload'
    }
  },
  renderer: {
    // 集成Vue插件
    plugins: [vue()],
    // 开发服务器配置
    server: {
      port: 5173,
      strictPort: true,  // 端口被占用时不自动切换
      open: false        // 不自动打开浏览器
    },
    build: {
      outDir: 'dist-renderer',
      // 静态资源处理
      assetsDir: 'assets',
      rollupOptions: {
        input: join(__dirname, 'src/index.html')
      }
    }
  }
})

4. 图标配置全平台方案

不同操作系统对图标有不同要求,推荐方案:

csharp 复制代码
// electron/main/index.js
import { BrowserWindow, nativeImage } from 'electron'
import { join } from 'path'
const getIconPath = () => {
  // 根据系统返回不同图标
  switch (process.platform) {
    case 'win32':
      return join(__dirname, '../../resources/icons/win/icon.ico')
    case 'darwin':
      return join(__dirname, '../../resources/icons/mac/icon.icns')
    default:
      return join(__dirname, '../../resources/icons/linux/icon.png')
  }
}
const createWindow = () => {
  const mainWindow = new BrowserWindow({
    icon: nativeImage.createFromPath(getIconPath()),
    // 其他配置...
  })
}

图标规范

  • Windows: .ico格式,至少包含 256x256 像素
  • macOS: .icns格式,建议包含 1024x1024 像素
  • Linux: .png格式,512x512 像素

5. 系统托盘高级实现

dart 复制代码
// electron/main/index.js
import { Tray, Menu, nativeImage, app, BrowserWindow } from 'electron'
import { join } from 'path'
let tray = null
let mainWindow = null
const createTray = () => {
  // 创建托盘图标
  const icon = nativeImage.createFromPath(join(__dirname, '../../resources/icons/tray.ico'))
    .resize({ width: 16, height: 16 }) // 托盘图标通常较小
  
  tray = new Tray(icon)
  
  // 托盘菜单模板
  const contextMenu = Menu.buildFromTemplate([
    {
      label: '显示窗口',
      click: () => {
        mainWindow.show()
        mainWindow.setSkipTaskbar(false) // 从任务栏显示
      }
    },
    {
      label: '隐藏窗口',
      click: () => {
        mainWindow.hide()
        mainWindow.setSkipTaskbar(true) // 从任务栏隐藏
      }
    },
    { type: 'separator' }, // 分隔线
    {
      label: '退出',
      click: () => {
        // 确保完全退出应用
        mainWindow.destroy()
        app.quit()
      }
    }
  ])
  
  tray.setContextMenu(contextMenu)
  tray.setToolTip('Electron Vue应用')
  
  // 双击托盘切换窗口显示状态
  tray.on('double-click', () => {
    if (mainWindow.isVisible()) {
      mainWindow.hide()
    } else {
      mainWindow.show()
    }
  })
}
// 在窗口创建后初始化托盘
app.whenReady().then(() => {
  mainWindow = new BrowserWindow(/* 配置 */)
  createTray()
})

三、打包与自动更新全流程

1. 打包环境准备

csharp 复制代码
# 安装打包工具
pnpm add electron-builder --save-dev
# 若使用原生模块(如C++扩展)
pnpm add electron-rebuild --save-dev

2. 打包配置详解

在package.json中添加:

kotlin 复制代码
{
  "build": {
    "appId": "com.yourcompany.yourapp", // 反向域名格式,唯一标识
    "productName": "你的应用名称",
    "copyright": "Copyright © 2024 ${author}",
    "asar": true, // 启用asar打包(保护源码)
    "directories": {
      "output": "release/${version}", // 输出到版本化目录
      "buildResources": "resources"   // 构建资源目录
    },
    "files": [
      "dist-electron/**/*",
      "dist-renderer/**/*"
    ],
    "win": {
      "icon": "resources/icons/win/icon.ico",
      "target": [
        {
          "target": "nsis",
          "arch": ["x64", "ia32"] // 支持32位和64位
        }
      ],
      "artifactName": "${productName}-v${version}-${arch}-setup.${ext}"
    },
    "nsis": {
      "oneClick": false, // 不启用一键安装
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "resources/icons/win/installer.ico",
      "uninstallerIcon": "resources/icons/win/uninstaller.ico",
      "installerHeaderIcon": "resources/icons/win/installer.ico",
      "createDesktopShortcut": true,
      "shortcutName": "${productName}"
    },
    "mac": {
      "icon": "resources/icons/mac/icon.icns",
      "target": ["dmg", "zip"],
      "artifactName": "${productName}-v${version}-${arch}.${ext}"
    },
    "linux": {
      "icon": "resources/icons/linux",
      "target": ["AppImage", "deb"],
      "artifactName": "${productName}-v${version}-${arch}.${ext}"
    }
  },
  "scripts": {
    "build": "electron-vite build && electron-builder",
    "build:win": "electron-vite build && electron-builder --win",
    "build:mac": "electron-vite build && electron-builder --mac",
    "build:linux": "electron-vite build && electron-builder --linux"
  }
}

3. 原生模块处理(C++ 库)

如果项目依赖 C++ 扩展(如 serialport、sqlite3 等),必须重新编译:

bash 复制代码
# 编译命令(Windows使用cmd执行)
./node_modules/.bin/electron-rebuild
# 或在package.json中添加脚本
"scripts": {
  "rebuild": "electron-rebuild"
}
# 执行:npm run rebuild

常见问题

  • 编译失败通常是因为缺少 C++ 编译工具链,Windows 需安装 Visual Studio Build Tools
  • macOS 需要安装 Xcode Command Line Tools: xcode-select --install
  • Linux 需要安装 build-essential: sudo apt-get install build-essential

4. 自动更新完整实现

1)安装依赖
csharp 复制代码
pnpm add electron-updater
2)主进程更新逻辑
javascript 复制代码
// electron/main/index.js
import { app, dialog, BrowserWindow } from 'electron'
import { autoUpdater } from 'electron-updater'
import log from 'electron-log'
// 配置日志
log.transports.file.level = 'info'
autoUpdater.logger = log
let mainWindow = null
// 初始化更新检查
const initUpdater = () => {
  // 生产环境才启用自动更新
  if (!app.isPackaged) {
    // 开发环境可指定测试更新服务器
    autoUpdater.setFeedURL({
      provider: 'generic',
      url: 'http://localhost:3000/updates/'
    })
  }
  // 检查更新
  autoUpdater.checkForUpdates().catch(err => {
    log.error('更新检查失败:', err)
  })
  // 发现新版本
  autoUpdater.on('update-available', (info) => {
    dialog.showMessageBox(mainWindow, {
      type: 'info',
      title: '发现新版本',
      message: `新版本 ${info.version} 已发布,是否下载更新?`,
      buttons: ['立即下载', '稍后再说']
    }).then(res => {
      if (res.response === 0) {
        // 用户选择下载
        mainWindow.webContents.send('update-start')
      }
    })
  })
  // 下载进度
  autoUpdater.on('download-progress', (progress) => {
    mainWindow.webContents.send('update-progress', {
      percent: Math.round(progress.percent),
      speed: Math.round(progress.bytesPerSecond / 1024) // KB/s
    })
  })
  // 下载完成
  autoUpdater.on('update-downloaded', () => {
    dialog.showMessageBox(mainWindow, {
      type: 'info',
      title: '更新就绪',
      message: '更新已下载完成,是否立即重启应用?',
      buttons: ['立即重启', '稍后重启']
    }).then(res => {
      if (res.response === 0) {
        autoUpdater.quitAndInstall()
      }
    })
  })
}
// 在应用就绪后初始化
app.whenReady().then(() => {
  mainWindow = new BrowserWindow(/* 配置 */)
  // 延迟检查更新,避免影响启动速度
  setTimeout(initUpdater, 5000)
})
3)更新服务器配置
  1. 在package.json中配置更新服务器地址:
json 复制代码
"build": {
  "publish": [
    {
      "provider": "generic",
      "url": "https://your-server.com/updates/"
    }
  ]
}
  1. 本地测试更新服务器:
bash 复制代码
# 安装静态服务器
pnpm add -g serve
# 在打包输出目录启动服务器
serve -s release/</doubaocanvas>
相关推荐
passerby606133 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了41 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅44 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税2 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore