Electron: создание и управление окнами

В Electron окна (Window) являются основным средством взаимодействия между приложением и пользователем.

Все видимые интерфейсы работают впроцессе рендеринга, а за создание и управление этими окнами отвечаетглавный процесс.


Создание и управление окнами

Electron с помощьюBrowserWindowкласса для создания окон.

В основном процессе новое окно определяется следующим образом:

Пример

const { BrowserWindow } = require('electron')

let mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  show: false, // Ждать, пока страница не будет готова, прежде чем отображать
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
  },
})

Параметры конфигурации BrowserWindow

  • width / height: Ширина и высота окна.
  • minWidth / minHeight: Ограничение минимального размера.
  • resizable: Разрешено ли изменение размера.
  • fullscreen / fullscreenable: Переходить ли в полноэкранный режим.
  • frame: Показывать ли системную строку заголовка (часто отключается при использовании пользовательской строки заголовка).
  • transparent: Прозрачное окно (позволяет достичь таких эффектов, как матовое стекло, парение и т. д.).
  • alwaysOnTop: Всегда ли окно находится поверх других окон.

Размер окна, положение и состояние
Состоянием окна можно управлять через API:

mainWindow.maximize()   // 最大化
mainWindow.minimize()   // 最小化
mainWindow.restore()    // 还原
mainWindow.setBounds({ x: 100, y: 100, width: 800, height: 600 }) // 调整位置和大小

Также можно прослушивать изменения окна:

mainWindow.on('resize', () => console.log('窗口尺寸变化'))
mainWindow.on('move', () => console.log('窗口移动'))

Окна без рамки и настраиваемая строка заголовка
Настройкиframe: falseПосле этого можно настроить область строки заголовка и имитировать перетаскивание с помощью CSS и JS:

.titlebar {
  -webkit-app-region: drag;
}

Учтите, что неперетаскиваемые области (например, кнопки) следует добавлять с помощью:

.titlebar {
  -webkit-app-region: drag;
}

Прослушивание событий окна

mainWindow.on('close', (e) => {
  e.preventDefault()
  mainWindow.hide()
})

Распространённые события включают:

  • ready-to-show: Страница завершила загрузку, и её можно безопасно отображать.
  • focus / blur: Окно получает или теряет фокус.
  • closed: Окно было уничтожено.

Загрузка содержимого страницы

Окна Electron могут загружать локальные HTML-файлы или удалённые веб-сайты.

Загрузка локальных HTML-файлов

mainWindow.loadFile('index.html')

Загрузка удалённого URL

mainWindow.loadURL('https://example.com')

Использование фронтенд-фреймворков
Если вы используете современные фреймворки, такие как Vue, React или Angular, вы можете загрузить собранные статические файлы в окно:

mainWindow.loadFile(path.join(__dirname, 'dist/index.html'))

Или загрузить локальный сервис в режиме разработки:

mainWindow.loadURL('http://localhost:5173')

Взаимодействие между окнами

В многоконных приложениях может потребоваться передавать данные между окнами или управлять поведением.

Связь родительского и дочернего окон
Можно указать при создании нового окнаparent:

let child = new BrowserWindow({
  parent: mainWindow,
  modal: true, // 模态窗口
  width: 400,
  height: 300,
})

Когда родительское окно закрывается, дочерние окна также автоматически закрываются.

Совместное использование данных между окнами
Вы можете использовать главный процесс в качестве посредника для передачи данных между различными процессами рендеринга или использовать глобальные объекты илиipcMain / ipcRenderer。

Передача сообщений между окнами
Главный процесс может напрямую управлять целевым окном:

child.webContents.send('update-data', someData)

Получение в дочернем окне:

const { ipcRenderer } = require('electron')
ipcRenderer.on('update-data', (event, data) => {
  console.log(data)
})

Оптимизация интерфейса

Экран-заставка
Когда главное окно загружается медленно, можно сначала показать лёгкое окно-заставку:

const splash = new BrowserWindow({ width: 400, height: 300, frame: false })
splash.loadFile('splash.html')

mainWindow.once('ready-to-show', () => {
  splash.close()
  mainWindow.show()
})

Оптимизация отображения окна
Распространённые методы предотвращения мерцания белого экрана включают:

  • Использованиеshow: falseСоздайте окно и после завершения загрузки,show()。
  • Установите цвет фона на отрисовываемой странице.
  • Предварительно загружайте ресурсы (иконки, шрифты, стили).

Методы предотвращения мерцания

  • Включите ускорение GPU или отключите аппаратное ускорение (в зависимости от ситуации).
  • Избегайте сложных анимаций или градиентов в CSS.
  • Используйте двойную буферизацию рендеринга для уменьшения дрожания при отрисовке.

Многооконное приложение Electron (включая страницу-заставку, основной интерфейс и дочерние окна)

Структура каталогов

my-electron-app/
├── package.json
├── main.js             # 主进程入口
├── preload.js          # 预加载脚本
├── splash.html         # 启动画面
├── index.html          # 主窗口页面
├── child.html          # 子窗口页面
└── renderer.js         # 渲染进程逻辑

main.js (главный процесс)

Пример

const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')

let mainWindow, splash, childWindow

function createSplash() {
  splash = new BrowserWindow({
    width: 400,
    height: 300,
    frame: false,
    transparent: true,
    alwaysOnTop: true,
  })
  splash.loadFile('splash.html')
}

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })

  mainWindow.loadFile('index.html')

  // Показать после завершения загрузки страницы
  mainWindow.once('ready-to-show', () => {
    if (splash) splash.close()
    mainWindow.show()
  })

  mainWindow.on('closed', () => (mainWindow = null))
}

function createChildWindow() {
  childWindow = new BrowserWindow({
    parent: mainWindow,
    modal: true,
    width: 400,
    height: 300,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })
  childWindow.loadFile('child.html')
}

// Межпроцессное взаимодействие
ipcMain.on('open-child', () => {
  if (!childWindow) createChildWindow()
})

ipcMain.handle('get-app-info', () => {
  return {
    name: app.getName(),
    version: app.getVersion(),
  }
})

app.on('ready', () => {
  createSplash()
  setTimeout(() => createMainWindow(), 1500)
})

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

preload.js(скрипт предзагрузки)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  openChild: () => ipcRenderer.send('open-child'),
  getAppInfo: () => ipcRenderer.invoke('get-app-info'),
})

index.html (главное окно)

Пример

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8" />
  <title>главное окно</title>
  <style>
    body { font-family: sans-serif; text-align: center; margin-top: 100px; }
    button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
  </style>
</head>
<body>
  <h1>Добро пожаловать в приложения Electron</h1>
  <p id="info"></p>
  <button id="open">Открыть дочернее окно</button>

  <script src="renderer.js"></script>
</body>
</html>

child.html (дочернее окно)

Пример

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>дочернее окно</title>
  <style>
    body { font-family: sans-serif; text-align: center; margin-top: 100px; }
  </style>
</head>
<body>
  <h2>Это дочернее окно</h2>
  <p>Может принимать команды из главного окна</p>
</body>
</html>

splash.html (Экран-заставка)

Пример

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>Загрузка...</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      background: linear-gradient(135deg, #8EC5FC, #E0C3FC);
      font-family: sans-serif;
    }
    h1 {
      font-size: 28px;
      color: #333;
    }
  </style>
</head>
<body>
  <h1>Приложение запускается, пожалуйста, подождите...</h1>
</body>
</html>

renderer.js (логика рендеринга)

Пример

const info = document.getElementById('info')
const btn = document.getElementById('open')

// Показать информацию о приложении
window.electronAPI.getAppInfo().then((appInfo) => {
  info.textContent =`Имя приложения:${appInfo.name}| Версия: ${appInfo.version}`
})

// Открыть дочернее окно
btn.addEventListener('click', () => {
  window.electronAPI.openChild()
})

Процесс после запуска выглядит следующим образом:

  1. Показывать при запускеЭкран-заставка(splash.html)。
  2. После завершения загрузки автоматически закрыть страницу-заставку и показатьглавное окно。
  3. Главное окно может открыватьдочернее окнос помощью кнопки, демонстрируя функциональность IPC-взаимодействия.
  4. Главное окно используетPreload для безопасного предоставления API, предотвращая прямой доступ к Node.js.
  5. Все окна используютпрослушивание событий、отложенное отображениеи другие стратегии оптимизации, чтобы избежать белых экранов и мерцания.
Другие расширения