Vue集成 #

一、项目搭建 #

1.1 使用 Vite 创建项目 #

bash
# 创建 Vue + TypeScript 项目
npm create vite@latest my-electron-vue -- --template vue-ts

# 进入项目目录
cd my-electron-vue

# 安装 Electron
npm install electron --save-dev
npm install electron-builder --save-dev

# 安装开发工具
npm install vite-plugin-electron --save-dev

1.2 项目结构 #

text
my-electron-vue/
├── electron/
│   ├── main.ts           # 主进程
│   ├── preload.ts        # 预加载脚本
│   └── utils/            # 工具函数
├── src/
│   ├── App.vue           # Vue 根组件
│   ├── main.ts           # 入口文件
│   ├── components/       # 组件目录
│   ├── composables/      # 组合式函数
│   ├── stores/           # 状态管理
│   ├── views/            # 页面组件
│   └── types/            # 类型定义
├── public/               # 静态资源
├── vite.config.ts        # Vite 配置
├── electron-builder.yml  # 打包配置
└── package.json

1.3 Vite 配置 #

typescript
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import electron from 'vite-plugin-electron';
import path from 'path';

export default defineConfig({
    plugins: [
        vue(),
        electron([
            {
                entry: 'electron/main.ts',
                onstart(options) {
                    options.startup();
                },
                vite: {
                    build: {
                        outDir: 'dist-electron',
                        rollupOptions: {
                            external: ['electron']
                        }
                    }
                }
            },
            {
                entry: 'electron/preload.ts',
                onstart(options) {
                    options.reload();
                },
                vite: {
                    build: {
                        outDir: 'dist-electron'
                    }
                }
            }
        ])
    ],
    resolve: {
        alias: {
            '@': path.resolve(__dirname, './src')
        }
    },
    base: './',
    build: {
        outDir: 'dist'
    }
});

二、主进程配置 #

2.1 主进程入口 #

typescript
// electron/main.ts
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';

const isDev = !app.isPackaged;

let mainWindow: BrowserWindow | null = null;

function createWindow() {
    mainWindow = new BrowserWindow({
        width: 1200,
        height: 800,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            nodeIntegration: false,
            contextIsolation: true
        }
    });

    if (isDev) {
        mainWindow.loadURL('http://localhost:5173');
        mainWindow.webContents.openDevTools();
    } else {
        mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
    }

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

app.whenReady().then(createWindow);

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

app.on('activate', () => {
    if (mainWindow === null) {
        createWindow();
    }
});

2.2 预加载脚本 #

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

contextBridge.exposeInMainWorld('electronAPI', {
    getAppInfo: () => ipcRenderer.invoke('get-app-info'),
    openFile: () => ipcRenderer.invoke('open-file'),
    saveFile: (content: string) => ipcRenderer.invoke('save-file', content)
});

三、Vue 集成 #

3.1 类型定义 #

typescript
// src/types/electron.d.ts
export interface ElectronAPI {
    getAppInfo: () => Promise<{ version: string; platform: string }>;
    openFile: () => Promise<string | null>;
    saveFile: (content: string) => Promise<boolean>;
}

declare global {
    interface Window {
        electronAPI: ElectronAPI;
    }
}

3.2 组合式函数 #

typescript
// src/composables/useElectron.ts
import { ref, onMounted } from 'vue';

export function useAppInfo() {
    const info = ref<{ version: string; platform: string } | null>(null);
    const loading = ref(true);

    onMounted(async () => {
        info.value = await window.electronAPI.getAppInfo();
        loading.value = false;
    });

    return { info, loading };
}

export function useFileOperations() {
    const openFile = async () => {
        return await window.electronAPI.openFile();
    };

    const saveFile = async (content: string) => {
        return await window.electronAPI.saveFile(content);
    };

    return { openFile, saveFile };
}

3.3 Vue 组件示例 #

vue
<!-- src/App.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import { useAppInfo, useFileOperations } from './composables/useElectron';

const { info, loading } = useAppInfo();
const { openFile, saveFile } = useFileOperations();

const content = ref('');

const handleOpen = async () => {
    const fileContent = await openFile();
    if (fileContent) {
        content.value = fileContent;
    }
};

const handleSave = async () => {
    await saveFile(content.value);
};
</script>

<template>
    <div class="app" v-if="!loading">
        <header>
            <h1>Electron + Vue App</h1>
            <p>Version: {{ info?.version }}</p>
            <p>Platform: {{ info?.platform }}</p>
        </header>
        
        <main>
            <div class="toolbar">
                <button @click="handleOpen">Open</button>
                <button @click="handleSave">Save</button>
            </div>
            
            <textarea
                v-model="content"
                placeholder="Enter text here..."
            />
        </main>
    </div>
    <div v-else>Loading...</div>
</template>

<style scoped>
.app {
    padding: 20px;
}

.toolbar {
    margin-bottom: 10px;
}

.toolbar button {
    margin-right: 10px;
}

textarea {
    width: 100%;
    height: 300px;
}
</style>

四、状态管理 #

4.1 使用 Pinia #

bash
npm install pinia
typescript
// src/stores/app.ts
import { defineStore } from 'pinia';

export const useAppStore = defineStore('app', {
    state: () => ({
        theme: 'light' as 'light' | 'dark',
        sidebarOpen: true
    }),
    
    actions: {
        setTheme(theme: 'light' | 'dark') {
            this.theme = theme;
        },
        
        toggleSidebar() {
            this.sidebarOpen = !this.sidebarOpen;
        }
    }
});

4.2 使用组件 #

vue
<!-- src/components/ThemeToggle.vue -->
<script setup lang="ts">
import { useAppStore } from '../stores/app';

const store = useAppStore();
</script>

<template>
    <button @click="store.setTheme(store.theme === 'light' ? 'dark' : 'light')">
        Current theme: {{ store.theme }}
    </button>
</template>

五、路由配置 #

5.1 安装 Vue Router #

bash
npm install vue-router

5.2 路由配置 #

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

const routes = [
    { path: '/', name: 'Home', component: Home },
    { 
        path: '/settings', 
        name: 'Settings', 
        component: () => import('../views/Settings.vue') 
    },
    { 
        path: '/about', 
        name: 'About', 
        component: () => import('../views/About.vue') 
    }
];

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

export default router;

5.3 入口文件 #

typescript
// src/main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './style.css';

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount('#app');

六、打包配置 #

6.1 package.json #

json
{
    "name": "my-electron-vue",
    "version": "1.0.0",
    "main": "dist-electron/main.js",
    "scripts": {
        "dev": "vite",
        "build": "vue-tsc && vite build && electron-builder",
        "preview": "vite preview"
    },
    "dependencies": {
        "vue": "^3.3.11",
        "vue-router": "^4.2.5",
        "pinia": "^2.1.7"
    },
    "devDependencies": {
        "@vitejs/plugin-vue": "^4.5.2",
        "electron": "^28.0.0",
        "electron-builder": "^24.9.1",
        "typescript": "^5.3.3",
        "vite": "^5.0.8",
        "vite-plugin-electron": "^0.28.0",
        "vue-tsc": "^1.8.25"
    }
}

6.2 electron-builder.yml #

yaml
appId: com.example.myapp
productName: My Electron Vue App

directories:
    output: release

files:
    - dist/**/*
    - dist-electron/**/*

mac:
    icon: build/icon.icns

win:
    icon: build/icon.ico

linux:
    icon: build/icon.png

七、最佳实践 #

7.1 开发建议 #

markdown
- [ ] 使用 TypeScript 类型安全
- [ ] 使用组合式 API
- [ ] 组件化开发
- [ ] Pinia 状态管理
- [ ] IPC 封装为组合式函数

7.2 性能优化 #

vue
<!-- 使用异步组件 -->
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';

const Settings = defineAsyncComponent(() => 
    import('./views/Settings.vue')
);
</script>

<template>
    <Suspense>
        <template #default>
            <Settings />
        </template>
        <template #fallback>
            <div>Loading...</div>
        </template>
    </Suspense>
</template>

八、总结 #

8.1 核心要点 #

要点 说明
Vite 推荐的构建工具
组合式 API Vue 3 推荐写法
IPC 封装 组合式函数封装
Pinia 推荐的状态管理

8.2 下一步 #

现在你已经掌握了 Vue 集成,接下来让我们学习 TypeScript支持,深入了解 Electron 的 TypeScript 开发!

最后更新:2026-03-28