Vue3与Electron集成
Vue3与Electron集成的优势
Vue3的Composition API和Electron的主进程/渲染进程架构天然契合。Vue3的响应式系统在Electron环境中表现稳定,特别是使用<script setup>
语法时,代码组织更加清晰。Electron的Node.js集成让Vue组件可以直接调用系统API,比如文件操作:
// 在Vue组件中直接使用Node.js模块
import fs from 'fs'
import { ref } from 'vue'
const fileContent = ref('')
function readFile(path) {
fileContent.value = fs.readFileSync(path, 'utf-8')
}
项目初始化配置
使用Vite创建基础项目结构比传统webpack更快:
npm create vite@latest electron-vue-app --template vue-ts
cd electron-vue-app
npm install electron electron-builder --save-dev
关键配置在vite.config.ts
中需要处理Electron的特殊需求:
export default defineConfig({
base: './', // 必须相对路径
build: {
outDir: 'dist',
emptyOutDir: false // 避免electron-builder清理
}
})
主进程与渲染进程通信
主进程文件electron/main.ts
典型结构:
import { app, BrowserWindow, ipcMain } from 'electron'
let win: BrowserWindow | null = null
app.whenReady().then(() => {
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
})
if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(process.env.VITE_DEV_SERVER_URL)
} else {
win.loadFile('dist/index.html')
}
})
ipcMain.handle('get-system-info', () => {
return {
platform: process.platform,
version: process.getSystemVersion()
}
})
渲染进程调用示例:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ipcRenderer } from 'electron'
const systemInfo = ref<any>(null)
onMounted(async () => {
systemInfo.value = await ipcRenderer.invoke('get-system-info')
})
</script>
处理开发环境与生产环境
Vite开发服务器需要与Electron主进程协同工作。在package.json
中添加脚本:
{
"scripts": {
"dev": "concurrently -k \"vite\" \"wait-on http://localhost:5173 && electron .\"",
"build": "vite build && electron-builder"
}
}
需要安装额外依赖:
npm install concurrently wait-on --save-dev
原生API集成实例
实现文件选择对话框的完整示例:
主进程添加处理程序:
import { dialog } from 'electron'
ipcMain.handle('open-file-dialog', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile']
})
return result.filePaths[0]
})
Vue组件调用:
<template>
<button @click="selectFile">选择文件</button>
<p>已选择:{{ selectedFile }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ipcRenderer } from 'electron'
const selectedFile = ref('')
const selectFile = async () => {
selectedFile.value = await ipcRenderer.invoke('open-file-dialog')
}
</script>
打包与分发配置
electron-builder
的基础配置:
{
"build": {
"appId": "com.example.electronvue",
"win": {
"target": "nsis",
"icon": "build/icon.ico"
},
"mac": {
"target": "dmg",
"icon": "build/icon.icns"
},
"linux": {
"target": "AppImage"
}
}
}
推荐的文件结构:
├── build/
│ ├── icon.icns
│ ├── icon.ico
│ └── background.png
├── electron/
│ ├── main.ts
│ └── preload.ts
├── src/
│ └── /* Vue项目标准结构 */
└── vite.config.ts
安全最佳实践
启用上下文隔离时的preload脚本示例:
electron/preload.ts
:
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
openFileDialog: () => ipcRenderer.invoke('open-file-dialog')
})
主进程配置修改:
new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 启用隔离
nodeIntegration: false // 禁用直接Node集成
}
})
Vue组件中安全调用方式:
declare global {
interface Window {
electronAPI: {
openFileDialog: () => Promise<string>
}
}
}
const selectFile = async () => {
selectedFile.value = await window.electronAPI.openFileDialog()
}
调试技巧
主进程调试配置:
{
"scripts": {
"debug": "electron --inspect=5858 ."
}
}
渲染进程调试时,在创建BrowserWindow时启用DevTools:
win = new BrowserWindow({
webPreferences: { devTools: true }
})
win.webContents.openDevTools()
Vite插件集成示例:
import electron from 'vite-plugin-electron'
export default defineConfig({
plugins: [
electron({
entry: 'electron/main.ts',
onstart(options) {
options.startup()
}
})
]
})
性能优化策略
使用Web Workers处理CPU密集型任务:
// worker.js
self.onmessage = (e) => {
const result = heavyCalculation(e.data)
self.postMessage(result)
}
// Vue组件中
const worker = new Worker('worker.js')
worker.postMessage(inputData)
worker.onmessage = (e) => {
processedData.value = e.data
}
主进程使用多个窗口时的内存管理:
const secondaryWindows = new Set()
function createSecondaryWindow() {
const win = new BrowserWindow(/*...*/)
secondaryWindows.add(win)
win.on('closed', () => secondaryWindows.delete(win))
}
原生菜单集成
创建应用菜单的完整示例:
import { Menu } from 'electron'
const template = [
{
label: '文件',
submenu: [
{
label: '打开',
click: () => win.webContents.send('menu-open-file')
},
{ type: 'separator' },
{ role: 'quit' }
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
Vue组件接收菜单事件:
<script setup>
import { ipcRenderer } from 'electron'
ipcRenderer.on('menu-open-file', async () => {
// 调用文件选择逻辑
})
</script>
自动更新实现
主进程更新逻辑:
import { autoUpdater } from 'electron-updater'
autoUpdater.autoDownload = false
autoUpdater.on('update-available', () => {
win.webContents.send('update-available')
})
ipcMain.handle('download-update', () => {
autoUpdater.downloadUpdate()
})
渲染进程UI交互:
<template>
<div v-if="updateStatus">
{{ updateStatus }}
<button @click="downloadUpdate">下载更新</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ipcRenderer } from 'electron'
const updateStatus = ref('')
ipcRenderer.on('update-available', () => {
updateStatus.value = '有新版本可用'
})
const downloadUpdate = () => {
ipcRenderer.invoke('download-update')
}
</script>
原生通知集成
系统通知的完整实现:
import { Notification } from 'electron'
function showNotification(title: string, body: string) {
new Notification({ title, body }).show()
}
ipcMain.handle('show-notification', (_, title, body) => {
showNotification(title, body)
})
Vue组件封装:
<script setup lang="ts">
function showNativeNotification(title: string, options?: NotificationOptions) {
if ('Notification' in window) {
new Notification(title, options)
} else {
ipcRenderer.invoke('show-notification', title, options.body)
}
}
</script>
多窗口管理
实现窗口管理器的示例:
class WindowManager {
private windows = new Map<string, BrowserWindow>()
createWindow(id: string, options: Electron.BrowserWindowConstructorOptions) {
if (this.windows.has(id)) {
this.windows.get(id)?.focus()
return
}
const win = new BrowserWindow(options)
this.windows.set(id, win)
win.on('closed', () => {
this.windows.delete(id)
})
}
}
Vue组件中打开新窗口:
<script setup>
import { ipcRenderer } from 'electron'
function openSettingsWindow() {
ipcRenderer.send('create-window', 'settings', {
width: 800,
height: 600,
modal: true
})
}
</script>
本地数据库集成
SQLite集成的完整方案:
npm install better-sqlite3 --save
主进程数据库服务:
import Database from 'better-sqlite3'
const db = new Database('app.db')
ipcMain.handle('query-db', (_, sql, params) => {
try {
const stmt = db.prepare(sql)
return params ? stmt.all(params) : stmt.all()
} catch (err) {
console.error(err)
return { error: err.message }
}
})
Vue组件中执行查询:
<script setup>
import { ref } from 'vue'
import { ipcRenderer } from 'electron'
const queryResults = ref([])
async function fetchData() {
queryResults.value = await ipcRenderer.invoke(
'query-db',
'SELECT * FROM users WHERE active = ?',
[1]
)
}
</script>
跨平台样式适配
处理平台差异的样式方案:
<template>
<div :class="[platform, isMaximized && 'maximized']">
<!-- 内容 -->
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ipcRenderer } from 'electron'
const platform = ref('')
const isMaximized = ref(false)
onMounted(() => {
platform.value = await ipcRenderer.invoke('get-platform')
ipcRenderer.on('window-maximized', () => {
isMaximized.value = true
})
ipcRenderer.on('window-unmaximized', () => {
isMaximized.value = false
})
})
</script>
<style>
.win32 {
font-family: 'Segoe UI', sans-serif;
}
.darwin {
font-family: -apple-system, sans-serif;
}
.maximized {
padding: 12px;
}
</style>
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:Vue3移动端解决方案