Proyecto práctico de Vue + Electron
En este capítulo usaremosVue + Electronpara crear una aplicación de notas; este proyecto práctico se llamaVueElectronNotesy sus funciones incluyen múltiples ventanas, bandeja del sistema, operaciones de archivos y comunicación por IPC. Es un proyecto muy adecuado para que los principiantes practiquen.
Si aún no conoces Vue, puedes consultar primero:Tutorial de Vue。
I. Funciones del proyecto
- Aplicación de escritorio para notas (Notes)
- La ventana principal muestra la lista de notas
- Abrir una ventana secundaria para crear una nota nueva
- Icono en la bandeja del sistema; minimizar a la bandeja
- Guardar/leer archivos localmente
- Comunicación segura entre el proceso de renderizado y el proceso principal
II. Inicialización del proyecto
1. Crear un proyecto Vue
vue create VueElectronNotes cd VueElectronNotes
Selecciona el preset predeterminado (Babel + ESLint).
2. Instalar Electron y sus dependencias
npm install --save-dev electron electron-builder concurrently wait-on npm install electron-store
electron-storeSe utiliza para la persistencia local de datos
III. Configuración del proceso principal
electron-main.js:
Ejemplo
const { app, BrowserWindow, Tray, Menu, ipcMain } = require('electron')
const path = require('path')
const Store = require('electron-store')
let mainWindow, noteWindow, tray
const store = new Store()
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
})
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:8080')
} else {
mainWindow.loadFile('dist/index.html')
}
mainWindow.on('minimize', (e) => {
e.preventDefault()
mainWindow.hide()
})
createTray()
}
function createNoteWindow() {
noteWindow = new BrowserWindow({
width: 400,
height: 300,
parent: mainWindow,
modal: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
})
noteWindow.loadFile('note.html')
}
function createTray() {
tray = new Tray(path.join(__dirname, 'assets/icon.png'))
const contextMenu = Menu.buildFromTemplate([
{ label: 'Mostrar ventana principal', click: () => mainWindow.show() },
{ label: 'Salir', click: () => app.quit() }
])
tray.setToolTip('VueElectronNotes')
tray.setContextMenu(contextMenu)
}
app.whenReady().then(createMainWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
ipcMain.handle('get-notes', () => {
return store.get('notes', [])
})
ipcMain.on('save-note', (event, note) => {
const notes = store.get('notes', [])
notes.push(note)
store.set('notes', notes)
mainWindow.webContents.send('update-notes', notes)
})
const path = require('path')
const Store = require('electron-store')
let mainWindow, noteWindow, tray
const store = new Store()
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
})
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:8080')
} else {
mainWindow.loadFile('dist/index.html')
}
mainWindow.on('minimize', (e) => {
e.preventDefault()
mainWindow.hide()
})
createTray()
}
function createNoteWindow() {
noteWindow = new BrowserWindow({
width: 400,
height: 300,
parent: mainWindow,
modal: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
})
noteWindow.loadFile('note.html')
}
function createTray() {
tray = new Tray(path.join(__dirname, 'assets/icon.png'))
const contextMenu = Menu.buildFromTemplate([
{ label: 'Mostrar ventana principal', click: () => mainWindow.show() },
{ label: 'Salir', click: () => app.quit() }
])
tray.setToolTip('VueElectronNotes')
tray.setContextMenu(contextMenu)
}
app.whenReady().then(createMainWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
ipcMain.handle('get-notes', () => {
return store.get('notes', [])
})
ipcMain.on('save-note', (event, note) => {
const notes = store.get('notes', [])
notes.push(note)
store.set('notes', notes)
mainWindow.webContents.send('update-notes', notes)
})
Explicación:
mainWindow: ventana principal, muestra la lista de notasnoteWindow: ventana secundaria, para crear notas nuevasTray: icono y menú de la bandeja del sistemaipcMain: el proceso principal recibe mensajes del proceso de renderizado
IV. Proceso de renderizado y Vue
preload.js:
Ejemplo
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
getNotes: () => ipcRenderer.invoke('get-notes'),
saveNote: (note) => ipcRenderer.send('save-note', note),
onUpdateNotes: (callback) => ipcRenderer.on('update-notes', callback)
})
contextBridge.exposeInMainWorld('electronAPI', {
getNotes: () => ipcRenderer.invoke('get-notes'),
saveNote: (note) => ipcRenderer.send('save-note', note),
onUpdateNotes: (callback) => ipcRenderer.on('update-notes', callback)
})
src/App.vue:
Ejemplo
<template>
<div>
<h1>Lista de notas</h1>
<button @click="openNoteWindow">Nueva nota</button>
<ul>
<li v-for="(note, index) in notes" :key="index">{{ note }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return { notes: [] }
},
methods: {
openNoteWindow() {
window.open('note.html', 'Nueva nota', 'width=400,height=300')
}
},
mounted() {
window.electronAPI.getNotes().then((notes) => {
this.notes = notes
})
window.electronAPI.onUpdateNotes((event, notes) => {
this.notes = notes
})
}
}
</script>
<div>
<h1>Lista de notas</h1>
<button @click="openNoteWindow">Nueva nota</button>
<ul>
<li v-for="(note, index) in notes" :key="index">{{ note }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return { notes: [] }
},
methods: {
openNoteWindow() {
window.open('note.html', 'Nueva nota', 'width=400,height=300')
}
},
mounted() {
window.electronAPI.getNotes().then((notes) => {
this.notes = notes
})
window.electronAPI.onUpdateNotes((event, notes) => {
this.notes = notes
})
}
}
</script>
note.html + note.js:
note.html:
Ejemplo
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Nueva nota</title></head>
<body>
<h2>Nueva nota</h2>
<textarea id="note"></textarea><br>
<button id="saveBtn">Guardar</button>
<script src="note.js"></script>
</body>
</html>
<html>
<head><meta charset="UTF-8"><title>Nueva nota</title></head>
<body>
<h2>Nueva nota</h2>
<textarea id="note"></textarea><br>
<button id="saveBtn">Guardar</button>
<script src="note.js"></script>
</body>
</html>
note.js:
Ejemplo
const saveBtn = document.getElementById('saveBtn')
saveBtn.addEventListener('click', () => {
const note = document.getElementById('note').value
if (note) {
window.electronAPI.saveNote(note)
window.close()
}
})
saveBtn.addEventListener('click', () => {
const note = document.getElementById('note').value
if (note) {
window.electronAPI.saveNote(note)
window.close()
}
})
Explicación:
note.htmlEs una ventana emergente para ingresar notasnote.jsLlamar aelectronAPI.saveNoteenviar al proceso principal- La ventana principal recibirá
update-notesel mensaje para actualizar la lista
V. Ejecutar el proyecto
Modo de desarrollo:
npm run electron:serve
- Vue Dev Server + Electron se inician simultáneamente
- Se puede depurar la interfaz de usuario en tiempo real
Empaquetar la aplicación:
npm run electron:build
- Genera el archivo ejecutable en
dist_electron - Compatible con Windows/macOS/Linux