Vue3 and Electron integration
Advantages of Vue3 and Electron Integration
Vue3's Composition API naturally aligns with Electron's main/renderer process architecture. Vue3's reactivity system performs stably in the Electron environment, especially when using the <script setup>
syntax, where code organization becomes clearer. Electron's Node.js integration allows Vue components to directly call system APIs, such as file operations:
// Using Node.js modules directly in Vue components
import fs from 'fs'
import { ref } from 'vue'
const fileContent = ref('')
function readFile(path) {
fileContent.value = fs.readFileSync(path, 'utf-8')
}
Project Initialization Configuration
Using Vite to create the project structure is faster than traditional webpack:
npm create vite@latest electron-vue-app --template vue-ts
cd electron-vue-app
npm install electron electron-builder --save-dev
Key configurations in vite.config.ts
to handle Electron-specific requirements:
export default defineConfig({
base: './', // Must use relative paths
build: {
outDir: 'dist',
emptyOutDir: false // Prevents electron-builder from cleaning
}
})
Main and Renderer Process Communication
Typical structure of the main process file 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()
}
})
Example of renderer process invocation:
<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>
Handling Development and Production Environments
The Vite development server needs to work in tandem with the Electron main process. Add scripts to package.json
:
{
"scripts": {
"dev": "concurrently -k \"vite\" \"wait-on http://localhost:5173 && electron .\"",
"build": "vite build && electron-builder"
}
}
Additional dependencies required:
npm install concurrently wait-on --save-dev
Native API Integration Example
Complete example of implementing a file selection dialog:
Add handler in the main process:
import { dialog } from 'electron'
ipcMain.handle('open-file-dialog', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile']
})
return result.filePaths[0]
})
Vue component invocation:
<template>
<button @click="selectFile">Select File</button>
<p>Selected: {{ 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>
Packaging and Distribution Configuration
Basic electron-builder
configuration:
{
"build": {
"appId": "com.example.electronvue",
"win": {
"target": "nsis",
"icon": "build/icon.ico"
},
"mac": {
"target": "dmg",
"icon": "build/icon.icns"
},
"linux": {
"target": "AppImage"
}
}
}
Recommended file structure:
├── build/
│ ├── icon.icns
│ ├── icon.ico
│ └── background.png
├── electron/
│ ├── main.ts
│ └── preload.ts
├── src/
│ └── /* Standard Vue project structure */
└── vite.config.ts
Security Best Practices
Example of a preload script with context isolation enabled:
electron/preload.ts
:
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
openFileDialog: () => ipcRenderer.invoke('open-file-dialog')
})
Modify main process configuration:
new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // Enable isolation
nodeIntegration: false // Disable direct Node integration
}
})
Secure invocation in Vue components:
declare global {
interface Window {
electronAPI: {
openFileDialog: () => Promise<string>
}
}
}
const selectFile = async () => {
selectedFile.value = await window.electronAPI.openFileDialog()
}
Debugging Techniques
Main process debugging configuration:
{
"scripts": {
"debug": "electron --inspect=5858 ."
}
}
For renderer process debugging, enable DevTools when creating BrowserWindow:
win = new BrowserWindow({
webPreferences: { devTools: true }
})
win.webContents.openDevTools()
Vite plugin integration example:
import electron from 'vite-plugin-electron'
export default defineConfig({
plugins: [
electron({
entry: 'electron/main.ts',
onstart(options) {
options.startup()
}
})
]
})
Performance Optimization Strategies
Using Web Workers for CPU-intensive tasks:
// worker.js
self.onmessage = (e) => {
const result = heavyCalculation(e.data)
self.postMessage(result)
}
// In Vue component
const worker = new Worker('worker.js')
worker.postMessage(inputData)
worker.onmessage = (e) => {
processedData.value = e.data
}
Memory management when using multiple windows in the main process:
const secondaryWindows = new Set()
function createSecondaryWindow() {
const win = new BrowserWindow(/*...*/)
secondaryWindows.add(win)
win.on('closed', () => secondaryWindows.delete(win))
}
Native Menu Integration
Complete example of creating an application menu:
import { Menu } from 'electron'
const template = [
{
label: 'File',
submenu: [
{
label: 'Open',
click: () => win.webContents.send('menu-open-file')
},
{ type: 'separator' },
{ role: 'quit' }
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
Vue component receiving menu events:
<script setup>
import { ipcRenderer } from 'electron'
ipcRenderer.on('menu-open-file', async () => {
// Call file selection logic
})
</script>
Auto-Update Implementation
Main process update logic:
import { autoUpdater } from 'electron-updater'
autoUpdater.autoDownload = false
autoUpdater.on('update-available', () => {
win.webContents.send('update-available')
})
ipcMain.handle('download-update', () => {
autoUpdater.downloadUpdate()
})
Renderer process UI interaction:
<template>
<div v-if="updateStatus">
{{ updateStatus }}
<button @click="downloadUpdate">Download Update</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ipcRenderer } from 'electron'
const updateStatus = ref('')
ipcRenderer.on('update-available', () => {
updateStatus.value = 'New version available'
})
const downloadUpdate = () => {
ipcRenderer.invoke('download-update')
}
</script>
Native Notification Integration
Complete implementation of system notifications:
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 component encapsulation:
<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>
Multi-Window Management
Example of a window manager implementation:
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)
})
}
}
Opening new windows in Vue components:
<script setup>
import { ipcRenderer } from 'electron'
function openSettingsWindow() {
ipcRenderer.send('create-window', 'settings', {
width: 800,
height: 600,
modal: true
})
}
</script>
Local Database Integration
Complete SQLite integration solution:
npm install better-sqlite3 --save
Main process database service:
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 }
}
})
Executing queries in Vue components:
<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>
Cross-Platform Style Adaptation
Styling solution for platform differences:
<template>
<div :class="[platform, isMaximized && 'maximized']">
<!-- Content -->
</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移动端解决方案