Кросс-платформенные приложения на Electron: от концепции до готового дистрибутива

Раздел: Кросс-платформенная разработка -> Десктопные приложения

Основной способ: приложение на чистом Electron без фреймворков

Исходная цель - создать минимальное окно браузера, которое работает как настольное приложение. В основе лежит Node.js и Chromium. Установка Electron выполняется через npm.

Как создать окно с загрузкой HTML-страницы?


mkdir my-electron-app
cd my-electron-app
npm init -y
npm install electron --save-dev
  

Electron js (разработка десктопного приложения на electron)

После установки создаётся файл main.js - точка входа в приложение. В нём импортируется модуль app и BrowserWindow. Создаётся окно с параметрами ширины и высоты, загружается файл index.html.


// main.js
const { app, BrowserWindow } = require('electron');

function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });
  win.loadFile('index.html');
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
  

Далее создаётся простой index.html:


<!DOCTYPE html>
<html>
<head><title>Моё приложение</title></head>
<body>
  <h1>Hello Electron!</h1>
</body>
</html>
  

Запуск приложения выполняется командой npx electron .

Типичные ошибки на этом этапе:

  • Ошибка "require is not defined" при использовании nodeIntegration: true и contextIsolation: true. Решение - установить nodeIntegration: true и contextIsolation: false, либо использовать preload-скрипты.
  • Ошибка "Electron failed to install correctly" - проблемы с правами доступа или архитектурой. Рекомендуется переустановить Electron с флагом --unsafe-perm или использовать npx.
  • Окно не отображается - проверяется наличие дисплея и параметры окна, также возможно требуется установить show: true.

Как интегрировать React, чтобы получить реактивную архитектуру?

React используется для построения пользовательского интерфейса. Настройка включает создание проекта React внутри Electron. Один из подходов - использование electron-react-boilerplate или ручная настройка с webpack.


mkdir electron-react-app
cd electron-react-app
npm init -y
npm install react react-dom
npm install --save-dev electron webpack webpack-cli babel-loader @babel/core @babel/preset-env @babel/preset-react html-webpack-plugin
  

Создаётся конфигурация webpack и Babel. В main.js загружается index.html, который уже собран webpack. В процессе разработки используется webpack-dev-server или просто сборка.

Как решить проблему двойного скролла или задержки при загрузке?

При использовании React с Electron часто возникает проблема с производительностью из-за множества ререндеров. Рекомендуется использовать React.memo, useMemo и ограничить nodeIntegration. Также при сборке для продакшена следует отключать DevTools.

Как использовать Vue, чтобы упростить шаблонизацию?

Vue.js - альтернатива React. Для интеграции удобно использовать electron-vue (устаревший) или vue-cli-plugin-electron-builder. Современный способ - создать проект Vue через Vite, затем добавить Electron.


npm create vite@latest my-vue-electron -- --template vue
cd my-vue-electron
npm install
npm install electron --save-dev
  

В корне создаётся electron/main.js. В package.json добавляется скрипт "electron:dev": "concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"".

Проблема: Vite использует ESM, а Electron работает с CommonJS. Для main.js лучше использовать CommonJS или настроить сборку. Также может возникнуть ошибка CORS при загрузке локальных ресурсов через dev-сервер. Решение - настроить webSecurity: false в окне или использовать file:// протокол.

Как собрать готовое приложение в дистрибутив с помощью electron-builder?

electron-builder - инструмент для упаковки приложения в .exe, .dmg, .deb и другие форматы. Установка: npm install --save-dev electron-builder. В package.json добавляется секция "build": { "appId": "com.example.app", "productName": "MyApp", "directories": { "output": "dist" } }.


npx electron-builder --win --x64
  

Сборка создаёт установщик в папке dist. Для macOS используется флаг --mac, для Linux --linux.

Ошибка: "Could not find any Electron packages" - нужно указать main файл и убедиться, что electron установлен как зависимость. Также при использовании native модулей требуется пересборка под целевую платформу.

Как использовать TypeScript для повышения надёжности кода?

TypeScript добавляет статическую типизацию. Установка: npm install typescript @types/node @types/electron --save-dev. Создаётся tsconfig.json. Файл main.ts компилируется в main.js. В package.json указывается "main": "dist/main.js". Используется tsc или ts-node для разработки.


// main.ts
import { app, BrowserWindow } from 'electron';
let mainWindow: BrowserWindow | null = null;
app.whenReady().then(() => {
  mainWindow = new BrowserWindow({ width: 800, height: 600 });
  mainWindow.loadFile('index.html');
});
  

Проблема: ts-node не работает с Electron из-за различий в модулях. Рекомендуется компилировать отдельно или использовать ts-node с флагом --compiler-options. Альтернатива - electron-forge с поддержкой TypeScript.

Расширенные примеры работы с Electron

Пример 1: IPC для обмена данными между главным и рендерным процессами

IPC (Inter-Process Communication) позволяет передавать сообщения между процессами. В главном процессе регистрируется обработчик, в рендерном вызывается.

Пример

// main.js
const { ipcMain, dialog } = require('electron');
ipcMain.handle('open-file', async (event) => {
  const result = await dialog.showOpenDialog({ properties: ['openFile'] });
  if (result.canceled) return null;
  return result.filePaths[0];
});

// renderer.js (в index.html)
const { ipcRenderer } = require('electron');
document.getElementById('btn').addEventListener('click', async () => {
  const filePath = await ipcRenderer.invoke('open-file');
  console.log('Выбран файл:', filePath);
});
При нажатии кнопки открывается системное диалоговое окно выбора файла. После выбора в консоль выводится путь к файлу.

Пример 2: Системный трей (Tray)

Трей - иконка в системной панели (панели задач). Позволяет управлять приложением без открытого окна.

Пример

const { Tray, Menu, nativeImage, app } = require('electron');
let tray = null;
app.whenReady().then(() => {
  const icon = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); // маленькая зелёная точка
  tray = new Tray(icon);
  const contextMenu = Menu.buildFromTemplate([
    { label: 'Показать окно', click: () => mainWindow.show() },
    { label: 'Выход', click: () => app.quit() }
  ]);
  tray.setToolTip('Моё приложение');
  tray.setContextMenu(contextMenu);
});
В системном трее появляется иконка. При клике правой кнопкой мыши отображается меню с опциями.

Пример 3: Автоматическое обновление через electron-updater

Библиотека electron-updater позволяет автоматически загружать и устанавливать обновления из репозитория (например, GitHub Releases).

Пример

const { autoUpdater } = require('electron-updater');
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on('update-available', (info) => {
  console.log('Доступно обновление', info.version);
});
autoUpdater.on('download-progress', (progressObj) => {
  console.log(`Скорость: ${progressObj.bytesPerSecond}, загружено: ${progressObj.percent}%`);
});
autoUpdater.on('update-downloaded', () => {
  autoUpdater.quitAndInstall();
});
При запуске приложение проверяет наличие обновлений на сервере. Если обновление найдено, загружается и после завершения автоматически устанавливается.

Пример 4: Создание меню приложения с акселераторами

Меню приложения можно полностью настроить, добавив горячие клавиши и подменю.

Пример

const { Menu, dialog, app } = require('electron');
const menuTemplate = [
  {
    label: 'Файл',
    submenu: [
      {
        label: 'Открыть',
        accelerator: 'CmdOrCtrl+O',
        click: () => { dialog.showMessageBox({ message: 'Открытие файла...' }); }
      },
      { type: 'separator' },
      { label: 'Выход', role: 'quit' }
    ]
  },
  {
    label: 'Помощь',
    submenu: [
      {
        label: 'О программе',
        click: () => { dialog.showMessageBox({ message: 'Версия 1.0', title: 'О программе' }); }
      }
    ]
  }
];
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
Приложение получает меню с пунктами "Файл" (с подменю "Открыть" и "Выход") и "Помощь" (с подменю "О программе"). Нажатие Ctrl+O (Cmd+O на macOS) вызывает диалоговое окно.

Разработка десктопного приложения на Electron - comments

En
Electron js (javascript)