从零到一:Electron + Vue 3 跨平台桌面应用开发完整指南

该文章已生成可运行项目,

从零到一:Electron + Vue 3 跨平台桌面应用开发完整指南

在这里插入图片描述

摘要:本文详细介绍了如何使用 Electron 和 Vue 3 构建跨平台桌面应用程序,包含完整的技术栈选型、项目搭建、功能实现、打包部署等环节,并分享了开发过程中遇到的常见问题及解决方案。适合有一定前端基础的开发者学习参考。


一、项目背景与技术选型

1.1 为什么选择 Electron

在企业级应用开发中,我们经常需要开发能够在 Windows、macOS 和 Linux 上运行的桌面应用。传统的桌面应用开发需要针对不同平台使用不同的技术栈(如 C#/.NET、Swift、Qt 等),开发成本高且维护困难。

Electron 的优势:

  • ✅ 一套代码,多平台运行(Windows、macOS、Linux)
  • ✅ 使用熟悉的 Web 技术栈(HTML、CSS、JavaScript)
  • ✅ 丰富的生态系统和社区支持
  • ✅ 可以调用 Node.js API,访问系统底层功能
  • ✅ 成熟的案例:VS Code、Discord、Slack、Figma 等

1.2 技术栈选择

经过调研和对比,最终确定了以下技术栈:

技术版本作用
Electron28.x跨平台桌面应用框架
Vue 33.4.x渐进式前端框架
Vite5.x下一代前端构建工具
Element Plus2.5.xUI 组件库
Pinia2.1.x状态管理
Vue Router4.2.x路由管理
Axios1.6.xHTTP 请求库

技术栈优势分析:

  • Vue 3:组合式 API、更好的 TypeScript 支持、性能提升
  • Vite:极速的冷启动、即时的热更新、真正的按需编译
  • Element Plus:企业级 UI 组件库,开箱即用
  • Pinia:轻量级、完整的 TypeScript 支持、模块化设计

在这里插入图片描述


二、项目初始化与环境搭建

2.1 环境准备

# 检查 Node.js 版本(建议使用 Node.js 20)
node -v  # v20.x.x

# 使用 nvm 切换 Node.js 版本(如果需要)
nvm install 20
nvm use 20

2.2 项目结构设计

一个清晰的项目结构是项目成功的基础:

ElectronDemo/
├── electron/                 # Electron 主进程代码
│   ├── main.js              # 主进程入口(窗口管理、生命周期)
│   └── preload.js           # 预加载脚本(安全通信桥梁)
├── src/                     # Vue 前端代码
│   ├── components/          # 公共组件
│   ├── pages/               # 页面组件
│   ├── stores/              # Pinia 状态管理
│   ├── router/              # 路由配置
│   ├── utils/               # 工具函数
│   ├── App.vue              # 根组件
│   └── main.js              # Vue 入口
├── build/                   # 打包资源(图标等)
├── dist/                    # 前端构建输出
├── release/                 # 应用打包输出
├── electron-builder.yml     # 打包配置
├── vite.config.js           # Vite 配置
└── package.json             # 项目配置

2.3 安装依赖

# 克隆或创建项目后,安装依赖
npm install

# 主要依赖说明
# 生产依赖
npm install vue@3.4.0 vue-router@4.2.5 pinia@2.1.7
npm install element-plus@2.5.0 axios@1.6.0
npm install @vueuse/core@10.9.0 three@0.182.0

# 开发依赖
npm install -D electron@28.2.0 electron-builder@24.9.1
npm install -D vite@5.0.0 @vitejs/plugin-vue@5.0.0
npm install -D concurrently@8.2.2

在这里插入图片描述


三、核心功能实现

3.1 Electron 主进程配置

Electron 应用的核心是主进程配置,需要处理窗口创建、环境判断、生命周期管理等。

electron/main.js 关键代码:

const { app, BrowserWindow } = require('electron')
const path = require('path')

function createWindow () {
  const win = new BrowserWindow({
    width: 1200,
    height: 700,
    resizable: true,
    title: '应用程序',
    webPreferences: { 
      nodeIntegration: false,
      contextIsolation: true
    }
  })
  
  // 环境判断:开发环境和生产环境使用不同的加载方式
  if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
    // 开发环境:加载 Vite 开发服务器
    win.loadURL('http://localhost:2255/')
    win.webContents.openDevTools() // 打开开发者工具
  } else {
    // 生产环境:加载打包后的 index.html
    win.loadFile(path.join(__dirname, '../dist/index.html'))
  }
}

// 应用准备就绪时创建窗口
app.whenReady().then(createWindow)

// macOS 特性:点击 dock 图标重新创建窗口
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

// 所有窗口关闭时退出应用(macOS 除外)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

关键点解析:

  1. 环境判断:开发环境加载本地服务器,生产环境加载打包文件
  2. 安全配置nodeIntegration: falsecontextIsolation: true 确保安全性
  3. 平台适配:macOS 平台特殊处理(dock 图标点击事件)

在这里插入图片描述

3.2 Vue 应用配置

src/main.js 完整配置:

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

import App from './App.vue'
import router from './router'

// 创建 Vue 应用
const app = createApp(App)

// 创建 Pinia 实例并配置持久化
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

// 注册插件
app.use(ElementPlus, { locale: zhCn })
app.use(pinia)
app.use(router)

// 移除页面滚动条,适配桌面应用
const style = document.createElement('style')
style.textContent = `
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  html, body {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
  #app {
    width: 100%;
    height: 100%;
  }
`
document.head.appendChild(style)

app.mount('#app')

3.3 全局水印组件实现

为了保护应用内容,实现了一个防篡改的全局水印组件:

src/components/Watermark.vue:

<template>
  <div class="watermark-container" ref="watermarkRef"></div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'

const props = defineProps({
  text: { type: String, default: '应用程序' },
  fontSize: { type: Number, default: 14 },
  color: { type: String, default: 'rgba(0, 0, 0, 0.06)' },
  rotate: { type: Number, default: -20 },
  gap: { type: Array, default: () => [120, 100] }
})

const watermarkRef = ref(null)
let observer = null

// 使用 Canvas 生成水印图案
const createWatermark = () => {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  
  const devicePixelRatio = window.devicePixelRatio || 1
  const fontSize = props.fontSize * devicePixelRatio
  
  canvas.width = (200 + props.gap[0]) * devicePixelRatio
  canvas.height = (fontSize + props.gap[1]) * devicePixelRatio
  
  ctx.translate(canvas.width / 2, canvas.height / 2)
  ctx.rotate((Math.PI / 180) * props.rotate)
  ctx.font = `${fontSize}px Arial`
  ctx.fillStyle = props.color
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'
  ctx.fillText(props.text, 0, 0)
  
  return canvas.toDataURL()
}

// 设置水印
const setWatermark = () => {
  if (!watermarkRef.value) return
  const watermarkUrl = createWatermark()
  watermarkRef.value.style.backgroundImage = `url(${watermarkUrl})`
}

// 防止删除水印的监听器
const setupObserver = () => {
  if (!watermarkRef.value) return
  
  observer = new MutationObserver(() => {
    setWatermark()
  })
  
  observer.observe(watermarkRef.value, {
    attributes: true,
    childList: true,
    subtree: true
  })
}

onMounted(() => {
  setWatermark()
  setupObserver()
  window.addEventListener('resize', setWatermark)
})

onBeforeUnmount(() => {
  if (observer) observer.disconnect()
  window.removeEventListener('resize', setWatermark)
})
</script>

<style scoped>
.watermark-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  background-repeat: repeat;
  z-index: 9999;
}
</style>

特性说明:

  • ✅ Canvas 绘制,性能优秀
  • ✅ MutationObserver 监听,防止删除
  • ✅ 响应式适配,支持窗口大小变化
  • ✅ 不影响用户交互(pointer-events: none

3.4 路由配置

使用 Hash 模式路由,适配 Electron 应用:

src/router/index.js:

import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/pages/Home/index.vue'),
    meta: { title: '首页' }
  },
  {
    path: '/tower-crane',
    name: 'TowerCrane',
    component: () => import('@/pages/TowerCrane/index.vue'),
    meta: { title: '塔吊监测' }
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
  document.title = to.meta.title || '应用程序'
  next()
})

export default router

为什么使用 Hash 模式?

  • Electron 打包后使用 file:// 协议加载文件
  • History 模式需要服务器支持,Hash 模式更适合本地文件系统

3.5 状态管理与持久化

使用 Pinia + 持久化插件:

src/stores/user.js:

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: null,
    token: '',
    permissions: []
  }),
  
  getters: {
    isLogin: (state) => !!state.token,
    userName: (state) => state.userInfo?.name || '未登录'
  },
  
  actions: {
    setUserInfo(info) {
      this.userInfo = info
    },
    setToken(token) {
      this.token = token
    },
    logout() {
      this.userInfo = null
      this.token = ''
      this.permissions = []
    }
  },
  
  // 持久化配置
  persist: {
    key: 'user-store',
    storage: localStorage,
    paths: ['userInfo', 'token'] // 只持久化指定字段
  }
})

四、应用打包与部署

4.1 图标准备

桌面应用需要为不同平台准备对应的图标:

生成 macOS 图标(.icns):

# 方法1:使用 macOS 自带工具
# 1. 准备一张 1024x1024 的 PNG 图片
# 2. 创建 iconset 目录并生成多尺寸图片
mkdir icon.iconset
sips -z 16 16 icon.png --out icon.iconset/icon_16x16.png
sips -z 32 32 icon.png --out icon.iconset/icon_16x16@2x.png
sips -z 32 32 icon.png --out icon.iconset/icon_32x32.png
sips -z 64 64 icon.png --out icon.iconset/icon_32x32@2x.png
sips -z 128 128 icon.png --out icon.iconset/icon_128x128.png
sips -z 256 256 icon.png --out icon.iconset/icon_128x128@2x.png
sips -z 256 256 icon.png --out icon.iconset/icon_256x256.png
sips -z 512 512 icon.png --out icon.iconset/icon_256x256@2x.png
sips -z 512 512 icon.png --out icon.iconset/icon_512x512.png
cp icon.png icon.iconset/icon_512x512@2x.png

# 3. 生成 icns 文件
iconutil -c icns icon.iconset
mv icon.icns build/

生成 Windows 图标(.ico):

# 使用 ImageMagick(需先安装:brew install imagemagick)
magick icon.png -define icon:auto-resize=256,128,64,48,32,16 build/icon.ico

4.2 打包配置

electron-builder.yml 完整配置:

appId: com.electron.vue3demo
productName: 应用程序
directories:
  output: release
  buildResources: build
files:
  - "dist/**/*"
  - "electron/**/*"
  - "package.json"

# macOS 配置
mac:
  target:
    - target: dmg
      arch:
        - x64
        - arm64
  category: public.app-category.utilities
  icon: build/icon.icns
  artifactName: "${productName}-${version}-mac-${arch}.${ext}"
  identity: null  # 跳过代码签名

# Windows 配置
win:
  target:
    - target: nsis
      arch:
        - x64
        - ia32
  icon: build/icon.ico
  artifactName: "${productName}-${version}-win-${arch}.${ext}"

# NSIS 安装程序配置
nsis:
  oneClick: false
  allowToChangeInstallationDirectory: true
  allowElevation: true
  installerIcon: build/icon.ico
  uninstallerIcon: build/icon.ico
  createDesktopShortcut: always
  createStartMenuShortcut: true
  shortcutName: ${productName}

# DMG 配置
dmg:
  contents:
    - x: 410
      y: 150
      type: link
      path: /Applications
    - x: 130
      y: 150
      type: file
  title: "${productName} ${version}"
  icon: build/icon.icns

4.3 打包命令

package.json 中的打包脚本:

{
  "scripts": {
    "dev": "vite",
    "electron:dev": "electron .",
    "dev:all": "concurrently \"npm run dev\" \"npm run electron:dev\"",
    "build": "vite build",
    "build:mac": "npm run build && electron-builder --mac",
    "build:win": "npm run build && electron-builder --win",
    "build:all": "npm run build && electron-builder --mac --win"
  }
}

执行打包(跳过代码签名):

# 打包 macOS 版本
CSC_IDENTITY_AUTO_DISCOVERY=false npm run build:mac

# 打包 Windows 版本
CSC_IDENTITY_AUTO_DISCOVERY=false npm run build:win

# 同时打包所有平台
CSC_IDENTITY_AUTO_DISCOVERY=false npm run build:all

打包输出:

在这里插入图片描述


五、常见问题与解决方案

5.1 打包后应用白屏问题

问题描述:
应用打包后双击运行,窗口显示白屏,没有内容。

原因分析:
开发环境和生产环境加载资源的方式不同:

  • 开发环境:加载 Vite 开发服务器(http://localhost:2255/
  • 生产环境:需要加载打包后的本地文件(dist/index.html

解决方案:

electron/main.js 中添加环境判断:

if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
  win.loadURL('http://localhost:2255/')
} else {
  win.loadFile(path.join(__dirname, '../dist/index.html'))
}

5.2 代码签名错误

问题描述:

error: The specified item could not be found in the keychain

原因分析:
打包时 electron-builder 默认会尝试对应用进行代码签名,但如果没有配置证书会报错。

解决方案:

  1. 开发/测试环境:跳过代码签名
CSC_IDENTITY_AUTO_DISCOVERY=false npm run build:mac
  1. 生产环境:配置代码签名证书
# electron-builder.yml
mac:
  identity: null  # 或配置实际的证书标识

5.3 依赖包错误

问题描述:

Could not resolve "universal-cookie" imported by "@vueuse/integrations"

原因分析:
@vueuse/integrations 支持多个第三方库集成,但这些库是可选依赖(peer dependencies),需要手动安装。

解决方案:

npm install universal-cookie

5.4 图标文件找不到

问题描述:

cannot find specified resource "build/icon.icns"

原因分析:
打包配置中指定了图标路径,但实际文件不存在。

解决方案:

  1. 确保图标文件存在:
ls -la build/icon.icns
ls -la build/icon.ico
  1. 如果没有图标,注释掉配置使用默认图标:
# icon: build/icon.icns  # 注释掉

5.5 Three.js 导出错误

问题描述:

"sRGBEncoding" is not exported by "node_modules/three/build/three.module.js"

原因分析:
Three.js 版本更新,API 发生变化。

解决方案:

更新导入方式:

// 旧版本
import { sRGBEncoding } from 'three'

// 新版本
import * as THREE from 'three'
// 使用 THREE.SRGBColorSpace

六、性能优化建议

6.1 前端性能优化

  1. 代码分割
// 路由懒加载
const Home = () => import('@/pages/Home/index.vue')
  1. 按需引入组件
// 按需引入 Element Plus
import { ElButton, ElTable } from 'element-plus'
  1. 图片优化
  • 使用 WebP 格式
  • 压缩图片尺寸
  • 懒加载图片

6.2 Electron 优化

  1. 减小打包体积
# electron-builder.yml
files:
  - "dist/**/*"
  - "electron/**/*"
  - "package.json"
  - "!**/node_modules/*/{CHANGELOG.md,README.md,README}"
  - "!**/node_modules/*/{test,__tests__,tests}"
  1. 启用 asar 打包
asar: true
  1. 配置镜像加速
# .npmrc
electron_mirror=https://npmmirror.com/mirrors/electron/

七、项目部署与分发

7.1 应用分发方式

  1. 直接分发安装包

    • macOS: .dmg 文件
    • Windows: .exe 安装程序
  2. 企业内部分发

    • 搭建内部下载服务器
    • 配置自动更新功能
  3. 应用商店发布

    • Mac App Store(需要 Apple Developer 账号)
    • Microsoft Store(需要开发者账号)

7.2 自动更新配置

使用 electron-updater 实现自动更新:

// main.js
const { autoUpdater } = require('electron-updater')

app.whenReady().then(() => {
  // 检查更新
  autoUpdater.checkForUpdatesAndNotify()
  
  autoUpdater.on('update-available', () => {
    console.log('发现新版本')
  })
  
  autoUpdater.on('update-downloaded', () => {
    console.log('下载完成,准备安装')
  })
})

八、最佳实践总结

8.1 开发规范

  1. 代码规范

    • 使用 ESLint + Prettier
    • 统一命名规范
    • 注释完善
  2. 目录结构

    • 按功能模块划分
    • 公共组件独立管理
    • 工具函数统一封装
  3. 版本管理

    • 使用语义化版本号
    • Git 提交规范
    • 分支管理策略

8.2 安全建议

  1. 禁用 Node 集成
webPreferences: {
  nodeIntegration: false,
  contextIsolation: true
}
  1. 使用 Content Security Policy
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  callback({
    responseHeaders: {
      ...details.responseHeaders,
      'Content-Security-Policy': ["default-src 'self'"]
    }
  })
})
  1. 验证外部链接
win.webContents.on('will-navigate', (event, url) => {
  if (!url.startsWith('http://localhost')) {
    event.preventDefault()
  }
})

8.3 调试技巧

  1. 开发者工具
// 开发环境自动打开
if (process.env.NODE_ENV === 'development') {
  win.webContents.openDevTools()
}
  1. 日志记录
// 使用 electron-log
const log = require('electron-log')
log.info('应用启动')
  1. 远程调试
# 启动时添加调试参数
electron . --remote-debugging-port=9222

九、项目展望

9.1 功能扩展方向

  • 集成数据库(SQLite/NeDB)
  • 实现应用内更新
  • 添加系统托盘功能
  • 支持多窗口管理
  • 集成 WebSocket 实时通信
  • 添加截图、录屏功能

9.2 技术升级

  • 迁移到 TypeScript
  • 集成单元测试(Vitest)
  • 添加 E2E 测试(Playwright)
  • 配置 CI/CD 自动化部署
  • 性能监控与错误追踪

十、总结

本文详细介绍了使用 Electron + Vue 3 构建跨平台桌面应用的完整流程,从技术选型、项目搭建、功能实现到打包部署,覆盖了开发过程中的各个环节。

核心要点回顾:

  1. 技术栈选型:Electron + Vue 3 + Vite + Element Plus
  2. 环境配置:主进程环境判断,开发/生产环境适配
  3. 功能实现:路由配置、状态管理、水印组件
  4. 打包部署:跨平台打包,代码签名处理
  5. 问题解决:常见问题排查与解决方案

项目优势:

  • 🎯 跨平台:一套代码,支持 Windows、macOS、Linux
  • 性能优秀:Vite 构建,快速开发体验
  • 🎨 UI 美观:Element Plus 企业级组件库
  • 🛡️ 安全可靠:完善的安全配置和水印防护
  • 📦 易于部署:自动化打包,简化分发流程

希望本文能够帮助你快速上手 Electron 桌面应用开发!如有问题欢迎在评论区交流讨论。


参考资源


作者简介:专注于前端技术和跨平台应用开发,欢迎关注我的 CSDN 博客,获取更多技术分享。

如果本文对你有帮助,请点赞👍、收藏⭐、关注➕支持一下!


标签Electron Vue3 桌面应用 跨平台开发 前端工程化

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈探索者chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值