Практический проект Vue + Electron
В этой главе мы будем использоватьVue + Electronдля создания приложения для заметок. Этот практический проект называетсяVueElectronNotes, а его функции охватывают многооконность, трей, файловые операции и IPC-взаимодействие. Это очень подходящий проект для практики начинающих.
Если вы ещё не знаете Vue, сначала можете обратиться к:Учебник по Vue。
I. Возможности проекта
- Настольное приложение для заметок (Notes)
- Главное окно отображает список заметок
- Всплывает дочернее окно для создания новых заметок
- Значок системного трея, сворачивание в трей
- Локальное сохранение/чтение файлов
- Безопасное взаимодействие между процессом рендеринга и главным процессом
II. Инициализация проекта
1. Создание проекта Vue
vue create VueElectronNotes cd VueElectronNotes
Выберите пресет по умолчанию (Babel + ESLint).
2. Установите Electron и зависимости
npm install --save-dev electron electron-builder concurrently wait-on npm install electron-store
electron-storeДля локального сохранения данных
III. Конфигурация главного процесса
electron-main.js:
Пример
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: 'Показать главное окно', click: () => mainWindow.show() },
{ label: 'Выход', click: () => app.Выход() }
])
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: 'Показать главное окно', click: () => mainWindow.show() },
{ label: 'Выход', click: () => app.Выход() }
])
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)
})
Описание:
mainWindow: Главное окно, отображает список заметокnoteWindow: Дочернее окно, используется для создания новых заметокTray: Значок и меню в системном трееipcMain: Главный процесс получает сообщения от процесса рендеринга
4. Процесс рендеринга и Vue
preload.js:
Пример
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:
Пример
<template>
<div>
<h1>список заметок</h1>
<button @click="openNoteWindow">Новая заметка</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', 'Новая заметка', 'width=400,height=300')
}
},
mounted() {
window.electronAPI.getNotes().then((notes) => {
this.notes = notes
})
window.electronAPI.onUpdateNotes((event, notes) => {
this.notes = notes
})
}
}
</script>
<div>
<h1>список заметок</h1>
<button @click="openNoteWindow">Новая заметка</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', 'Новая заметка', '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:
Пример
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Новая заметка</title></head>
<body>
<h2>Новая заметка</h2>
<textarea id="note"></textarea><br>
<button id="saveBtn">Сохранить</button>
<script src="note.js"></script>
</body>
</html>
<html>
<head><meta charset="UTF-8"><title>Новая заметка</title></head>
<body>
<h2>Новая заметка</h2>
<textarea id="note"></textarea><br>
<button id="saveBtn">Сохранить</button>
<script src="note.js"></script>
</body>
</html>
note.js:
Пример
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()
}
})
Описание:
note.htmlэто всплывающее окно для ввода заметокnote.jsВызовelectronAPI.saveNoteОтправить в главный процесс- Главное окно получит его
update-notesСообщение обновляет список
V. Запуск проекта
Режим разработки:
npm run electron:serve
- Запуск Vue Dev Server + Electron одновременно
- Возможность отладки UI в реальном времени
Упаковка приложения:
npm run electron:build
- Вывод исполняемого файла в
dist_electron - Поддерживает Windows/macOS/Linux