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>
相关推荐
CF14年老兵1 分钟前
2025年我最爱的免费编程学习资源宝库 💻
前端·后端·trae
北京_宏哥10 分钟前
🔥《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
java·前端·面试
王维志14 分钟前
⏱ TimeSpan:C#时间间隔结构
前端·后端·c#·.net
阿幸软件杂货间22 分钟前
【最新版】Edge浏览器(官方版)安装包_Edge浏览器(官方版)安装教程
前端·edge
RaidenLiu31 分钟前
Flutter 状态管理:Provider 入门与实战
前端·flutter
隔壁老王z35 分钟前
设计实现一个Web 终端:基于 Vue 3 和 Xterm.js 的实践
前端·iterm
中微子36 分钟前
简单介绍跨域资源共享(CORS)
前端
極光未晚40 分钟前
Vue 项目 webpack 打包体积分析:从 “盲猜优化” 到 “精准瘦身”
前端·vue.js·性能优化
刘小筛1 小时前
Ant Design Vue (2x) 按钮(button)单击后离开,按钮状态无变化
前端
mogullzr1 小时前
4.1.ByteOJ用户模块——登录注册功能(RSA + TimeStamp加密过)
前端·后端