Skip to content

Vue + Electron,打造跨平台桌面应用

· 7 min

技术栈简介#

一句话:Vue 管“好看好用”,Electron 管“能干能装”,打包交给 electron-builder,这仨配起来就差不多打通任督二脉了。


环境准备:别在门口摔跤#

初始化(任选其一):

Terminal window
# 方案 A:electron-vite(推荐上手)
npm create electron-vite@latest my-app
# 选 Vue 或 Vue + TypeScript
# 方案 B:已有 Vite + Vue 项目,叠加 Electron
pnpm add -D electron vite-plugin-electron electron-builder

桌面应用开发实战:把“壳”和“页面”跑起来#

目录心智(常见三件套):

/electron/
main.ts # 主进程入口(创建窗口)
preload.ts # 预加载脚本(桥接 API)
/src/ # Vue 渲染进程
main.ts
App.vue

主进程(electron/main.ts

import { app, BrowserWindow } from 'electron'
import path from 'node:path'
const isDev = !app.isPackaged
let win: BrowserWindow | null = null
async function createWindow() {
win = new BrowserWindow({
width: 1100,
height: 760,
minWidth: 960,
title: 'Vue + Electron',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true, // 安全第一
},
})
if (isDev) {
await win.loadURL('http://localhost:5173')
win.webContents.openDevTools({ mode: 'detach' })
} else {
win.removeMenu()
await win.loadFile(path.join(__dirname, '../renderer/index.html'))
}
}
app.whenReady().then(createWindow)
app.on('window-all-closed', () => process.platform !== 'darwin' && app.quit())
app.on('activate', () => BrowserWindow.getAllWindows().length === 0 && createWindow())

预加载(electron/preload.ts

import { contextBridge, ipcRenderer } from 'electron'
// 只暴露你愿意给到渲染进程的“白名单”API
contextBridge.exposeInMainWorld('api', {
ping: () => ipcRenderer.invoke('ping'),
openFile: (filters?: Electron.FileFilter[]) => ipcRenderer.invoke('dialog:openFile', filters),
})

Vue 里用(src/main.ts

import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

页面里调用(src/App.vue

<script setup lang="ts">
const hi = async () => {
const res = await window.api.ping()
console.log(res)
}
</script>
<template>
<div class="p-6">
<h1>Vue + Electron</h1>
<button @click="hi">Ping 主进程</button>
</div>
</template>

开发命令(以 electron-vite 为例,原样可跑):

Terminal window
pnpm dev
# 会同时起 Electron 主进程 + Vite 渲染进程

IPC 通信:说话要走正道#

主进程开门(ipcMain.handle):

electron/main.ts
import { ipcMain, dialog } from 'electron'
ipcMain.handle('ping', () => 'pong from main')
ipcMain.handle('dialog:openFile', async (_evt, filters) => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters,
})
return canceled ? null : filePaths[0]
})

渲染进程通过预加载桥接(上文 preload.ts 已暴露):

// window.api.ping() / window.api.openFile()

要点(别嫌啰嗦,都是血泪):


自动更新:吃过一次香一整年#

通用方案:electron-builder + electron-updater

安装:

Terminal window
pnpm add -D electron-builder electron-updater

主进程里加(autoUpdater):

electron/main.ts
import { autoUpdater } from 'electron-updater'
import { dialog } from 'electron'
function setupAutoUpdate() {
autoUpdater.autoDownload = true
autoUpdater.on('update-available', () => {
console.log('发现新版本,开始下载…')
})
autoUpdater.on('update-downloaded', () => {
const res = dialog.showMessageBoxSync({
type: 'info',
buttons: ['重启更新', '稍后'],
title: '更新完成',
message: '新版本已准备就绪,是否立即重启应用?',
defaultId: 0,
cancelId: 1,
})
if (res === 0) autoUpdater.quitAndInstall()
})
autoUpdater.on('error', (e) => console.error('更新失败:', e))
// 触发检查
autoUpdater.checkForUpdatesAndNotify()
}
app.whenReady().then(() => {
setupAutoUpdate()
createWindow()
})

发布端(最省心是 GitHub Releases):


打包:三平台一锅端#

electron-builder 配置(放 package.jsonelectron-builder.yml

最小可用(package.json 片段):

{
"name": "vue-electron-demo",
"version": "1.0.0",
"main": "dist/electron/main.js",
"build": {
"appId": "com.example.vue_electron_demo",
"productName": "VueElectronDemo",
"directories": {
"output": "release"
},
"files": [
"dist/**",
"dist-electron/**",
"!**/*.map"
],
"mac": {
"target": ["dmg", "zip"],
"category": "public.app-category.productivity",
"hardenedRuntime": true,
"entitlements": "entitlements.mac.plist",
"entitlementsInherit": "entitlements.mac.plist"
},
"win": {
"target": ["nsis", "zip"]
},
"linux": {
"target": ["AppImage", "deb"],
"category": "Utility"
},
"publish": [
{ "provider": "github" }
]
},
"scripts": {
"dev": "electron-vite dev",
"build": "electron-vite build",
"build:app": "electron-builder",
"release": "electron-builder --publish always"
}
}

签名与发布小抄:

体积与性能:


加餐:常见坑与处理#


小结#

用 Vue + Electron 开桌面端,其实就是把“前端开发体验”搬进桌面世界。开发期手感顺滑,能力边界也足够宽;真正上路,记得在 IPC 安全、自动更新、打包签名这些“看起来琐碎”的地方多花一分钟,后面能省一堆小时。要是遇到必须“忍痛割爱”的环节(比如某些巨无霸依赖),先想想有没有更轻的替代;能省的体积就别硬撑,真撑也是压榨自己。