Skip to content

Electron 桌面应用模板

基于 Electron + Vue 3 + TypeScript + Vite 的现代化桌面应用开发模板,开箱即用,让你快速构建跨平台桌面应用。

技术栈

  • Electron - 使用 Web 技术构建跨平台桌面应用
  • Vue 3 - 渐进式 JavaScript 框架
  • TypeScript - JavaScript 的超集,提供类型安全
  • Vite - 下一代前端构建工具
  • Vue Router - Vue.js 官方路由管理器(Hash 模式)
  • Pinia - Vue 3 官方状态管理库
  • Vue i18n - Vue.js 国际化插件
  • Tailwind CSS - 实用优先的 CSS 框架
  • SCSS - CSS 预处理器,增强样式编写能力
  • Electron Forge - 应用打包和分发工具

快速开始

创建项目

bash
# 初始化项目
vup init my-desktop-project

# 进入项目目录
cd my-desktop-project

# 添加 Electron 应用
vup add my-electron-app
# 选择 Electron 桌面应用模板

开发

bash
# 安装依赖
pnpm install

# 启动 Electron 开发服务器
cd apps/my-electron-app
pnpm dev

Electron 应用将自动启动,支持热重载。

构建

bash
# 构建应用
pnpm build

# 制作安装包
pnpm make

项目结构

apps/my-electron-app/
├── src/
│   ├── main.ts                    # 主进程入口
│   ├── preload.ts                 # 预加载脚本
│   └── renderer/                  # 渲染进程(Vue 应用)
│       ├── assets/                # 资源文件
│       │   └── images/            # 图片文件
│       ├── locales/               # 国际化文件
│       │   ├── index.ts           # i18n 配置
│       │   ├── en_US.ts           # 英文翻译
│       │   └── zh_CN.ts           # 中文翻译
│       ├── router/                # Vue Router(Hash 模式)
│       │   ├── index.ts           # 路由配置
│       │   └── routes.ts          # 路由定义
│       ├── views/                 # 页面组件
│       │   ├── demo/              # 示例页面
│       │   ├── docs/              # 文档页面
│       │   ├── empty/             # 空状态页面
│       │   └── index/             # 首页
│       ├── App.vue                # 根组件
│       ├── main.ts                # 渲染进程入口
│       └── index.css              # 全局样式
├── .vite/                         # Vite 构建缓存
├── out/                           # 构建输出目录
├── forge.config.ts                # Electron Forge 配置
├── vite.main.config.ts            # 主进程 Vite 配置
├── vite.preload.config.ts         # 预加载脚本 Vite 配置
├── vite.renderer.config.ts        # 渲染进程 Vite 配置
├── package.json                   # 项目配置
└── tsconfig.json                  # TypeScript 配置

核心特性

🖥️ Electron 主进程

typescript
// src/main.ts
import { app, BrowserWindow } from 'electron';
import { createWindow } from './window';

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

🔒 预加载脚本

typescript
// src/preload.ts
import { contextBridge, ipcRenderer } from 'electron';

// 安全地暴露 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  // 示例 API
  getVersion: () => ipcRenderer.invoke('get-version'),
});

🎯 Vue 3 渲染进程

vue
<template>
  <div class="container">
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>
    <button @click="increment">计数: {{ count }}</button>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const count = ref(0);
const title = ref('Electron + Vue 3 应用');

const description = computed(() => `当前计数是 ${count.value}`);

const increment = () => {
  count.value++;
};
</script>

🛣️ Hash 模式路由

重要:Electron 应用必须使用 Hash 模式路由,不能使用 History 模式。

typescript
// router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router';

export default createRouter({
  history: createWebHashHistory(), // 使用 Hash 模式
  routes,
});

原因

  • Electron 应用运行在 file:// 协议下
  • History 模式需要服务器支持,但 Electron 没有传统意义上的服务器
  • Hash 模式通过 URL 的 hash 部分来管理路由,不依赖服务器

🎨 桌面优化样式

vue
<template>
  <div class="min-h-screen bg-gray-100">
    <header class="bg-white shadow-sm">
      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <h1 class="text-3xl font-bold text-gray-900">
          {{ $t('common.title') }}
        </h1>
      </div>
    </header>

    <main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
      <div class="px-4 py-6 sm:px-0">
        <div class="border-4 border-dashed border-gray-200 rounded-lg h-96">
          <p class="text-center text-gray-500 mt-20">
            {{ $t('common.description') }}
          </p>
        </div>
      </div>
    </main>
  </div>
</template>

开发工具

Vite 配置

模板使用分离的 Vite 配置来处理不同的进程:

typescript
// vite.main.config.ts - 主进程
export default defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/main.ts'),
      formats: ['cjs'],
      fileName: () => 'main.js',
    },
    rollupOptions: {
      external: ['electron'],
    },
  },
});

// vite.renderer.config.ts - 渲染进程
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia', 'vue-i18n'],
      dts: path.resolve(__dirname, 'auto-imports.d.ts'),
      vueTemplate: true,
    }),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@shared': path.resolve(__dirname, '../../_shared'),
    },
  },
});

Electron Forge 配置

typescript
// forge.config.ts
import type { ForgeConfig } from '@electron-forge/shared-types';

const config: ForgeConfig = {
  packagerConfig: {
    asar: true,
  },
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {},
    },
    {
      name: '@electron-forge/maker-zip',
      platforms: ['darwin'],
    },
    {
      name: '@electron-forge/maker-deb',
      config: {},
    },
    {
      name: '@electron-forge/maker-rpm',
      config: {},
    },
  ],
  plugins: [
    {
      name: '@electron-forge/plugin-vite',
      config: {
        build: [
          {
            entry: 'src/main.ts',
            config: 'vite.main.config.ts',
          },
          {
            entry: 'src/preload.ts',
            config: 'vite.preload.config.ts',
          },
          {
            entry: 'src/renderer/main.ts',
            config: 'vite.renderer.config.ts',
          },
        ],
      },
    },
  ],
};

export default config;

可用脚本

json
{
  "scripts": {
    "dev": "electron-forge start",
    "build": "electron-forge package",
    "make": "electron-forge make",
    "publish": "electron-forge publish",
    "lint": "eslint src/ --ext .vue,.ts,.js",
    "lint:fix": "eslint src/ --ext .vue,.ts,.js --fix",
    "format": "prettier --write \"src/**/*.{js,ts,vue,json,css,scss}\"",
    "format:check": "prettier --check \"src/**/*.{js,ts,vue,json,css,scss}\""
  }
}

构建和分发

开发构建

bash
# 启动开发服务器,支持热重载
pnpm dev

生产构建

bash
# 打包应用
pnpm build

创建安装包

bash
# 创建平台特定的安装包
pnpm make

这将创建以下格式的安装包:

  • Windows: .exe 安装包
  • macOS: .zip 压缩包
  • Linux: .deb.rpm

发布应用

bash
# 发布到分发平台
pnpm publish

Monorepo 项目注意事项

重要:在 monorepo 项目中,Electron 的二进制文件可能安装不成功。如果遇到以下错误:

Error: Electron failed to install correctly, please delete node_modules/electron and try installing again

这是 monorepo 项目的常见问题,请按以下步骤解决:

解决方案

  1. 清理并重新安装

    bash
    # 删除 Electron 相关文件
    rm -rf node_modules/electron
    
    # 重新安装
    pnpm install electron --force
  2. 🔥 关键步骤:手动运行 Electron 安装脚本

    这一步非常重要! Electron 的二进制文件需要手动下载:

    bash
    # 进入 Electron 目录
    cd node_modules/electron
    
    # 手动运行安装脚本(下载二进制文件)
    node install.js

    说明:在 monorepo 环境中,pnpm 的符号链接机制可能导致 Electron 的安装脚本没有正确执行,因此需要手动运行 install.js 来下载平台特定的二进制文件。

  3. 验证安装

    bash
    # 检查 Electron 版本(应该显示版本号而不是错误)
    npx electron --version
  4. 启动应用

    bash
    # 回到项目根目录
    cd ../..
    
    # 启动 Electron 应用
    pnpm dev

5. 如果仍然失败

如果上述步骤仍然失败,尝试完全清理:

bash
# 完全清理
rm -rf node_modules pnpm-lock.yaml

# 清理 pnpm 缓存
pnpm store prune

# 重新安装
pnpm install

# 手动运行 Electron 安装脚本
cd node_modules/electron && node install.js

# 回到项目根目录
cd ../..

# 启动应用
pnpm dev

最佳实践

🔒 安全性

  • 使用 contextBridge 进行安全的 IPC 通信
  • 在渲染进程中禁用 Node.js 集成
  • 使用预加载脚本安全地暴露 API
  • 验证所有用户输入

⚡ 性能优化

  • 使用 Vite 进行快速开发和优化构建
  • 为大型应用实现代码分割
  • 优化图片和资源文件
  • 使用 Electron 内置的性能工具

🌍 跨平台开发

  • 在所有目标平台上进行测试
  • 优雅地处理平台特定功能
  • 使用 Electron 的平台检测 API
  • 遵循平台特定的 UI 指南

🎨 桌面应用设计

  • 桌面端优化的用户界面
  • 适配不同分辨率的显示器
  • 键盘和鼠标友好的交互设计
  • 优化的桌面应用性能

相关资源