Electron: как создать десктопное приложение на веб-технологиях
Для кого эта статья:
- Веб-разработчики, желающие расширить свои навыки десктопной разработки.
- Новички в программировании, заинтересованные в создании приложений с использованием технологий JavaScript, HTML и CSS.
Разработчики, стремящиеся создать кроссплатформенные приложения с использованием платформы Electron.
Разработка десктопных приложений с использованием веб-технологий — это как получить сверхспособность для веб-разработчика. Electron позволяет создавать кроссплатформенные приложения, используя JavaScript, HTML и CSS, но без погружения в специфику различных операционных систем. От простых утилит до сложных программ вроде Visual Studio Code и Slack — всё это построено на Electron. Следуя этому руководству, вы пройдёте путь от идеи до готового десктопного приложения, даже если ранее работали только с браузерными технологиями. 🚀
Хотите серьезно погрузиться в мир современной веб-разработки? Курс Обучение веб-разработке от Skypro даст вам мощную базу JavaScript, HTML и CSS — ключевых технологий для создания приложений на Electron. Вы освоите не только веб-разработку, но и получите навыки, необходимые для создания полноценных десктопных приложений. От верстки до работы с API — всё, что нужно для старта в Electron!
Что такое Electron: знакомство с технологией
Electron — это фреймворк, созданный GitHub (ныне принадлежащим Microsoft), который позволяет разрабатывать нативные приложения для настольных операционных систем с использованием веб-технологий. В сердце Electron лежат два мощных движка: Chromium для отображения пользовательского интерфейса и Node.js для доступа к файловой системе и другим ресурсам операционной системы.
Ключевая особенность Electron заключается в его способности объединять веб-фронтенд с возможностями десктопного приложения, что даёт разработчикам лучшее из обоих миров. 💡
| Компонент | Функция | Преимущества |
|---|---|---|
| Chromium | Отрисовка пользовательского интерфейса | Современные веб-стандарты, высокая производительность рендеринга |
| Node.js | Взаимодействие с ОС и файловой системой | Доступ к нативным API, возможность использовать npm-пакеты |
| V8 | Выполнение JavaScript кода | Высокая производительность, JIT-компиляция |
Архитектура Electron-приложения базируется на двух типах процессов:
- Main Process (Главный процесс) — контролирует жизненный цикл приложения, создаёт окна и управляет системными событиями
- Renderer Process (Процесс рендеринга) — отвечает за отображение UI и обработку взаимодействия с пользователем
Коммуникация между этими процессами осуществляется через IPC (Inter-Process Communication), что обеспечивает безопасность и стабильность приложения.
Популярность Electron стремительно растёт — ежегодно число приложений на этой платформе увеличивается на 20-30%. Такие гиганты как Slack, Skype, VS Code, Figma Desktop, Twitch и Spotify используют Electron, что говорит о зрелости и надёжности технологии.
Максим Волков, Lead Frontend Developer Когда наша команда получила задачу создать инструмент для аналитиков, которым было неудобно постоянно работать через браузер, Electron стал очевидным выбором. Мы уже имели веб-версию со всей бизнес-логикой на JavaScript. За три недели мы перенесли функционал в десктопное приложение, добавили офлайн-режим и интеграцию с локальными файлами. Самым сложным оказалось не написание кода, а понимание концепции разделения на Main и Renderer процессы. Когда эта архитектура прояснилась, всё встало на свои места — и теперь мы используем Electron для всех внутренних инструментов.

Настройка среды для разработки приложения на Electron
Прежде чем приступить к созданию Electron-приложения, необходимо настроить среду разработки. Процесс настройки включает установку Node.js, npm (или Yarn), редактора кода и самого Electron. Минимальные требования к оборудованию относительно скромные — любой современный компьютер подойдёт для разработки. 🛠️
Шаг 1: Установка Node.js и npm
Скачайте и установите актуальную LTS-версию Node.js с официального сайта nodejs.org. Вместе с Node.js автоматически устанавливается npm. Проверьте успешность установки, выполнив в терминале:
node -v
npm -v
Шаг 2: Выбор редактора кода
Для разработки Electron-приложений рекомендуется использовать Visual Studio Code, который имеет отличную интеграцию с JavaScript и Node.js экосистемой. Установите полезные расширения:
- ESLint — для анализа кода и выявления потенциальных ошибок
- Debugger for Chrome — для отладки Electron-приложений
- npm Intellisense — для автодополнения npm модулей
- Electron Snippets — для быстрого добавления шаблонного кода
Шаг 3: Создание базовой структуры проекта
Создайте новую директорию для проекта и инициализируйте npm:
mkdir my-electron-app
cd my-electron-app
npm init -y
Шаг 4: Установка Electron
Добавьте Electron как зависимость разработки:
npm install electron --save-dev
Обновите файл package.json, добавив скрипт для запуска приложения:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Моё первое приложение на Electron",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^25.0.0"
}
}
Нередко возникают проблемы с загрузкой бинарных файлов Electron из-за сетевых ограничений или медленного соединения. В таких случаях можно использовать зеркала или параметры настройки npm:
| Проблема | Решение | Команда/Конфигурация |
|---|---|---|
| Таймаут загрузки | Увеличить время ожидания npm | npm config set timeout 600000 |
| Проблемы с прокси | Настроить прокси для npm | npm config set proxy http://proxy-server:port |
| Медленная загрузка | Использовать зеркало CNPM | ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" |
Шаг 5: Настройка инструментов для разработки
Для более эффективной разработки рекомендуется настроить:
- ESLint для проверки качества кода
- Prettier для форматирования
- nodemon для автоматического перезапуска приложения при изменениях
Установите эти инструменты:
npm install --save-dev eslint prettier nodemon
Базовая структура проекта теперь готова, и мы можем переходить к созданию основных файлов приложения в следующем разделе. ✅
Создаем структуру проекта для первого приложения
После настройки среды разработки пришло время создать структуру проекта и основные файлы для нашего Electron-приложения. Правильная организация файлов с самого начала сэкономит время и поможет избежать проблем при масштабировании проекта. 📁
Для простого приложения на Electron нам потребуются следующие файлы:
main.js— главный процесс приложенияindex.html— основной HTML-файл для пользовательского интерфейсаrenderer.js— скрипт для процесса рендерингаpreload.js— скрипт предварительной загрузки для безопасной коммуникации между процессамиstyles.css— стили для пользовательского интерфейса
Создадим базовую структуру каталогов:
my-electron-app/
├── package.json
├── main.js
├── preload.js
├── renderer.js
└── src/
├── index.html
└── styles/
└── styles.css
Начнем с создания файла main.js, который является точкой входа в приложение:
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
// Сохраняем глобальную ссылку на объект окна
let mainWindow;
function createWindow() {
// Создаем окно браузера
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
// Загружаем HTML-файл в окно
mainWindow.loadFile(path.join(__dirname, 'src', 'index.html'));
// Открываем DevTools при необходимости
// mainWindow.webContents.openDevTools();
// Обработка закрытия окна
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// Создаем окно, когда приложение готово
app.on('ready', createWindow);
// Выход из приложения, когда все окна закрыты (кроме macOS)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// На macOS пересоздаем окно при активации приложения
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
Теперь создадим файл preload.js, который будет служить мостом между процессами:
const { contextBridge, ipcRenderer } = require('electron');
// Экспортируем API для использования в процессе рендеринга
contextBridge.exposeInMainWorld('electronAPI', {
// Пример функции для отправки сообщения в главный процесс
sendMessage: (message) => ipcRenderer.send('message', message),
// Пример функции для получения ответа от главного процесса
onResponse: (callback) => ipcRenderer.on('response', callback)
});
Далее, создадим файл src/index.html для пользовательского интерфейса:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Моё первое Electron-приложение</title>
<link rel="stylesheet" href="styles/styles.css">
</head>
<body>
<div class="container">
<h1>Привет, Electron!</h1>
<p>Это моё первое приложение на Electron.</p>
<button id="send-btn">Отправить сообщение</button>
<div id="response"></div>
</div>
<script src="../renderer.js"></script>
</body>
</html>
И, наконец, создадим файл renderer.js для обработки пользовательского интерфейса:
// Обработка событий пользовательского интерфейса
document.addEventListener('DOMContentLoaded', () => {
const sendBtn = document.getElementById('send-btn');
const responseDiv = document.getElementById('response');
// Обработчик клика по кнопке
sendBtn.addEventListener('click', () => {
// Отправляем сообщение в главный процесс через API, экспортированный в preload.js
window.electronAPI.sendMessage('Привет из рендерера!');
});
// Обработчик для получения ответа от главного процесса
window.electronAPI.onResponse((event, response) => {
responseDiv.textContent = response;
});
});
Добавим простые стили в src/styles/styles.css:
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
}
button {
background-color: #4285f4;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-top: 10px;
}
button:hover {
background-color: #3367d6;
}
#response {
margin-top: 20px;
padding: 10px;
background-color: #f9f9f9;
border-left: 4px solid #4285f4;
}
Обновим файл main.js, чтобы добавить обработчики IPC-сообщений:
// Добавим в существующий main.js
ipcMain.on('message', (event, message) => {
console.log('Получено сообщение:', message);
// Отправляем ответ обратно в рендерер
mainWindow.webContents.send('response', `Ответ на: ${message}`);
});
Антон Карпов, Senior Frontend Developer Когда я впервые начал работать с Electron, самым сложным для меня было понять, как правильно организовать структуру проекта. В нашем стартапе мы разрабатывали аналитический инструмент для локального анализа данных. Я потратил два дня на попытки соединить main и renderer процессы, и только потом осознал важность preload-скриптов для безопасной коммуникации. Нам даже пришлось переписать часть кода из-за неправильной архитектуры. Совет начинающим: уделите особое внимание безопасной архитектуре с самого начала — разделение процессов в Electron существует не просто так. Правильная структура файлов и понимание жизненного цикла приложения сэкономят вам дни работы.
Разработка пользовательского интерфейса и функционала
Теперь, когда базовая структура приложения готова, приступим к разработке пользовательского интерфейса и функциональности. На этом этапе мы расширим наше простое приложение, добавив полезные функции и улучшив UI. 🎨
Для примера, создадим приложение-заметки с возможностью сохранения и загрузки заметок из файловой системы. Это позволит продемонстрировать взаимодействие с ОС — ключевое преимущество Electron.
Обновим наш index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Electron Заметки</title>
<link rel="stylesheet" href="styles/styles.css">
</head>
<body>
<div class="container">
<h1>Electron Заметки</h1>
<div class="controls">
<button id="new-note">Новая заметка</button>
<button id="save-note">Сохранить</button>
<button id="load-note">Загрузить</button>
</div>
<div class="editor-container">
<textarea id="note-content" placeholder="Введите текст заметки..."></textarea>
</div>
<div id="status" class="status"></div>
</div>
<script src="../renderer.js"></script>
</body>
</html>
Обновим стили в styles.css:
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 0;
background-color: #f5f5f5;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
width: 80%;
max-width: 800px;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 20px;
}
.controls {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
button {
background-color: #4285f4;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
button:hover {
background-color: #3367d6;
}
.editor-container {
margin-bottom: 15px;
}
#note-content {
width: 100%;
height: 300px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
font-size: 16px;
resize: none;
box-sizing: border-box;
}
.status {
padding: 10px;
margin-top: 10px;
border-radius: 4px;
font-size: 14px;
text-align: center;
}
.status-success {
background-color: #d4edda;
color: #155724;
}
.status-error {
background-color: #f8d7da;
color: #721c24;
}
Расширим preload.js, добавив функции для работы с файлами:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// Сохранение заметки
saveNote: (content) => ipcRenderer.invoke('save-note', content),
// Загрузка заметки
loadNote: () => ipcRenderer.invoke('load-note'),
// Создание новой заметки
newNote: () => ipcRenderer.send('new-note')
});
Обновим renderer.js для работы с новыми API:
document.addEventListener('DOMContentLoaded', () => {
const noteContent = document.getElementById('note-content');
const newNoteBtn = document.getElementById('new-note');
const saveNoteBtn = document.getElementById('save-note');
const loadNoteBtn = document.getElementById('load-note');
const statusDiv = document.getElementById('status');
// Отображение статусного сообщения
function showStatus(message, isError = false) {
statusDiv.textContent = message;
statusDiv.className = 'status ' + (isError ? 'status-error' : 'status-success');
// Скрываем сообщение через 3 секунды
setTimeout(() => {
statusDiv.textContent = '';
statusDiv.className = 'status';
}, 3000);
}
// Обработчик для создания новой заметки
newNoteBtn.addEventListener('click', () => {
noteContent.value = '';
showStatus('Создана новая заметка');
});
// Обработчик для сохранения заметки
saveNoteBtn.addEventListener('click', async () => {
const content = noteContent.value;
if (!content.trim()) {
showStatus('Заметка пуста!', true);
return;
}
try {
const result = await window.electronAPI.saveNote(content);
showStatus(`Заметка сохранена: ${result}`);
} catch (error) {
showStatus(`Ошибка при сохранении: ${error}`, true);
}
});
// Обработчик для загрузки заметки
loadNoteBtn.addEventListener('click', async () => {
try {
const content = await window.electronAPI.loadNote();
if (content) {
noteContent.value = content;
showStatus('Заметка загружена');
}
} catch (error) {
showStatus(`Ошибка при загрузке: ${error}`, true);
}
});
});
Теперь обновим main.js, добавив функции для работы с файловой системой:
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 900,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
mainWindow.loadFile(path.join(__dirname, 'src', 'index.html'));
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
// Обработчик для сохранения заметки
ipcMain.handle('save-note', async (event, content) => {
const { canceled, filePath } = await dialog.showSaveDialog({
title: 'Сохранить заметку',
defaultPath: path.join(app.getPath('documents'), 'моя_заметка.txt'),
filters: [
{ name: 'Text Files', extensions: ['txt'] },
{ name: 'All Files', extensions: ['*'] }
]
});
if (canceled) return 'Операция отменена';
try {
fs.writeFileSync(filePath, content, 'utf-8');
return path.basename(filePath);
} catch (error) {
console.error('Ошибка при сохранении:', error);
throw error.message;
}
});
// Обработчик для загрузки заметки
ipcMain.handle('load-note', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
title: 'Загрузить заметку',
defaultPath: app.getPath('documents'),
filters: [
{ name: 'Text Files', extensions: ['txt'] },
{ name: 'All Files', extensions: ['*'] }
],
properties: ['openFile']
});
if (canceled || filePaths.length === 0) return null;
try {
return fs.readFileSync(filePaths[0], 'utf-8');
} catch (error) {
console.error('Ошибка при загрузке:', error);
throw error.message;
}
});
// Обработчик для создания новой заметки
ipcMain.on('new-note', () => {
// Этот обработчик не требует действий в main process,
// так как очистка поля выполняется в renderer process
});
Это базовое приложение-заметка демонстрирует несколько ключевых аспектов разработки на Electron:
- Создание пользовательского интерфейса с использованием HTML/CSS
- Обработка пользовательских событий в renderer процессе
- Безопасная коммуникация между процессами через preload-скрипт
- Доступ к нативным API (файловая система, диалоги) из main процесса
При разработке Electron-приложения важно придерживаться ряда лучших практик:
| Практика | Описание | Почему важно |
|---|---|---|
| Изоляция контекста | Использование contextIsolation: true | Предотвращает XSS-атаки и повышает безопасность |
| Preload-скрипты | Четко определенный API для коммуникации | Разделение ответственности между процессами |
| Асинхронные IPC | Использование invoke/handle вместо send/on | Более надежное управление асинхронными операциями |
| Обработка ошибок | try/catch для всех операций с файлами/сетью | Предотвращает падение приложения при ошибках |
Наше приложение уже функционально, но при желании его можно расширить, добавив функции форматирования текста, автосохранение, тёмную тему и другие улучшения. 🌟
Сборка и распространение приложения на разных ОС
Финальный этап разработки Electron-приложения — сборка и подготовка его к распространению среди пользователей. Electron позволяет создавать инсталляторы для всех основных операционных систем из одной кодовой базы, что является его главным преимуществом. 🚢
Для сборки и упаковки приложения мы будем использовать electron-builder — популярный инструмент, который автоматизирует многие сложности процесса сборки. Он создаёт исполняемые файлы, настраивает метаданные приложения и генерирует инсталляторы.
Шаг 1: Установка electron-builder
npm install electron-builder --save-dev
Шаг 2: Настройка конфигурации сборки
Добавьте следующую конфигурацию в файл package.json:
{
"name": "electron-notes",
"version": "1.0.0",
"description": "Простое приложение для заметок на Electron",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"build-win": "electron-builder --windows",
"build-mac": "electron-builder --mac",
"build-linux": "electron-builder --linux"
},
"author": "Ваше имя",
"license": "MIT",
"devDependencies": {
"electron": "^25.0.0",
"electron-builder": "^24.4.0"
},
"build": {
"appId": "com.yourname.electronnotes",
"productName": "Electron Notes",
"directories": {
"output": "dist"
},
"files": [
"main.js",
"preload.js",
"renderer.js",
"src/**/*",
"package.json"
],
"win": {
"target": ["nsis"],
"icon": "src/assets/icon.ico"
},
"mac": {
"target": ["dmg"],
"icon": "src/assets/icon.icns"
},
"linux": {
"target": ["AppImage", "deb"],
"category": "Utility",
"icon": "src/assets/icon.png"
}
}
}
Шаг 3: Подготовка иконок
Для профессионального внешнего вида приложения необходимо создать иконки для каждой платформы. Создайте директорию src/assets и добавьте следующие файлы:
icon.ico— для Windows (размеры 16x16, 32x32, 48x48, 256x256)icon.icns— для macOS (можно создать из PNG с помощью утилитыiconutil)icon.png— для Linux (минимум 512x512 пикселей)
Если у вас нет опыта создания иконок, можно воспользоваться онлайн-инструментами, такими как iconfinder.com или flaticon.com, а затем конвертировать их в нужные форматы с помощью converticon.com.
Шаг 4: Сборка приложения для разных платформ
Теперь можно запустить сборку для нужной платформы:
- Для Windows:
npm run build-win - Для macOS:
npm run build-mac - Для Linux:
npm run build-linux
Для сборки всех платформ сразу (если ОС поддерживает): npm run build
После завершения сборки в директории dist появятся инсталляторы для выбранных платформ.
Шаг 5: Оптимизация размера приложения
Electron-приложения имеют относительно большой размер из-за включения полной версии Chromium и Node.js. Для оптимизации размера можно:
- Использовать
electron-builderс опциейcompression: "maximum" - Применить
asarупаковку, которая объединяет файлы приложения в единый архив - Исключить ненужные ресурсы и модули с помощью настройки
files
Добавьте в конфигурацию build:
"build": {
// ...существующая конфигурация
"compression": "maximum",
"asar": true
}
Шаг 6: Автообновление приложения
Для поддержки автоматических обновлений можно использовать electron-updater. Установите его:
npm install electron-updater
И добавьте код для проверки обновлений в main.js:
const { autoUpdater } = require('electron-updater');
// В функции createWindow или после неё
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on('update-available', () => {
mainWindow.webContents.send('update-available');
});
autoUpdater.on('update-downloaded', () => {
mainWindow.webContents.send('update-downloaded');
});
Шаг 7: Подписание приложения
Для повышения доверия пользователей и предотвращения предупреждений безопасности, особенно в Windows и macOS, рекомендуется подписывать приложение цифровой подписью:
- Для Windows потребуется сертификат Authenticode (можно получить через Microsoft или такие сервисы, как Digicert)
- Для macOS нужен Developer ID Certificate из Apple Developer Program
Настройка подписи в package.json:
"build": {
// ...существующая конфигурация
"win": {
"target": ["nsis"],
"icon": "src/assets/icon.ico",
"certificateFile": "path/to/certificate.pfx",
"certificatePassword": "YOUR_PASSWORD"
},
"mac": {
"target": ["dmg"],
"icon": "src/assets/icon.icns",
"identity": "YOUR_DEVELOPER_ID"
}
}
Шаг 8: Распространение приложения
После создания инсталляторов у вас есть несколько вариантов распространения:
- Прямая загрузка — разместите инсталляторы на вашем веб-сайте
- Магазины приложений — Microsoft Store, Mac App Store (требует соответствия дополнительным требованиям)
- GitHub Releases — популярный способ распространения с поддержкой автообновления
- Специализированные сервисы — Softonic, Uptodown, для Linux — репозитории пакетов
Для GitHub Releases настройте electron-builder:
"build": {
// ...существующая конфигурация
"publish": [{
"provider": "github",
"owner": "your-github-username",
"repo": "your-repo-name"
}]
}
После этой настройки можно публиковать новые версии командой:
npm run build -- -p always
Поздравляем! Теперь ваше Electron-приложение готово к распространению. Помните, что первое впечатление очень важно, поэтому уделите внимание деталям: иконкам, скриншотам, описанию и удобной процедуре установки. 🏆
Создание десктопного приложения на Electron — мощный инструмент в арсенале веб-разработчика. Следуя этому руководству, вы прошли все этапы: от настройки среды разработки до сборки готового к распространению приложения. Главное преимущество Electron в том, что он позволяет использовать привычные веб-технологии для создания полноценных кроссплатформенных приложений. Не бойтесь экспериментировать, добавлять новые функции и оптимизировать производительность. Каждое Electron-приложение, которое вы создаёте, делает вас более универсальным и востребованным специалистом в мире современной разработки.