React + Electron: практический проект

В этой главе мы будем использовать React + Electron для создания приложения для заметок. Этот практический проект называется ReactElectronNotes, а его возможности включают поддержку нескольких окон, трей, файловые операции и IPC-взаимодействие. Это очень подходящий проект для начинающих для практики.

Если вы ещё не знакомы с React, можете обратиться к:Учебник по React。

ReactElectronNotesПроект научит вас создавать с нулякроссплатформенное настольное приложение для заметок, включая следующие возможности:

  • Рендеринг фронтенда React
  • Управление главным процессом Electron
  • IPC-взаимодействие (главный процесс ↔ процесс рендеринга)
  • Локальное сохранение данных заметок (electron-store)
  • Интеграция с системным треем и меню

Описание структуры проекта

ReactElectronNotes/
│
├── src/                  # React 源码目录
├── build/                # 打包后的 React 静态文件
├── electron-main.js      # Electron 主进程
├── preload.js            # 渲染与主进程通信桥
├── note.html             # 子窗口页面
├── note.js               # 子窗口脚本
├── package.json
└── assets/
    └── icon.png          # 托盘图标

I. Обзор функций проекта

Функциональный модуль Описание
главное окно Отображение списка всех заметок
дочернее окно Ввод новой заметки
Local Storage Использованиеelectron-storeСохранение данных
Трей Сворачивание в системный трей, нажатие для вызова главного интерфейса
IPC-взаимодействие Процесс рендеринга взаимодействует с главным процессом для чтения и записи данных.

2. Инициализация проекта

1. Создание проекта React

npx create-react-app ReactElectronNotes
cd ReactElectronNotes

2. Установите Electron и его зависимости

npm install --save-dev electron electron-builder concurrently wait-on
npm install electron-store

3. Изменитеpackage.json

Убедитесь, чтоscriptsДобавьте команду запуска Electron в:

"main": "electron-main.js",
"scripts": {
  "react-start": "react-scripts start",
  "react-build": "react-scripts build",
  "electron-start": "concurrently \"npm run react-start\" \"wait-on http://localhost:3000 && electron .\"",
  "electron-build": "react-scripts build && electron-builder"
}

3. Создайте главный процесс Electron

Новый файл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:3000')
  } else {
    mainWindow.loadFile(path.join(__dirname, 'build/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(path.join(__dirname, 'note.html'))
}

function createTray() {
  tray = new Tray(path.join(__dirname, 'assets/icon.png'))
  const menu = Menu.buildFromTemplate([
    { label: Показать окно, click: () => mainWindow.show() },
    { label: 'Выход', click: () => app.Выход() }
  ])
  tray.setToolTip('ReactElectronNotes')
  tray.setContextMenu(menu)
}

app.whenReady().then(createMainWindow)

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)
})

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

4. Создайте скрипт предзагрузки

Новая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Безопасно предоставьте API процессу рендеринга.


V. Интерфейс фронтенда React

src/App.js

Пример

import React, { useState, useEffect } from 'react'

function App() {
  const [notes, setNotes] = useState([])

  useEffect(() => {
    window.electronAPI.getNotes().then(setNotes)
    window.electronAPI.onUpdateNotes((_event, newNotes) => setNotes(newNotes))
  }, [])

  const openNoteWindow = () => {
    window.open('note.html', Новая заметка, 'width=400,height=300')
  }

  return (
    <div style={{ padding: 20 }}>
      <h1>Мои заметки</h1>
      <button onClick={openNoteWindow}>Новая заметка</button>
      <ul>
        {notes.map((n, i) => (
          <li key={i}>{n}</li>
        ))}
      </ul>
    </div>
  )
}

export default App

Новаяnote.htmlиnote.js

note.html

Пример

<!DOCTYPE html>
<html lang="zh">
<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.trim()) {
    window.electronAPI.saveNote(note)
    window.close()
  }
})

6. Запуск и упаковка

Запуск среды разработки

npm run electron-start

После открытия появится главное окно React. Вы можете нажать "Новая заметка", чтобы открыть окно ввода.

Упаковка и публикация

npm run electron-build

Будет создавать кросс-платформенные исполняемые файлы (.exe、.app、.AppImageи т.д.).


7. Направления расширения

Особенности Подход
Поддержка Markdown Использованиеreact-markdownРендеринг содержимого
Поиск данных Сторона React черезfilter()Реализовать локальный поиск
Автосохранение Слушать поле вводаonChangeАвтосохранение
облачная синхронизация Интегрировать Firebase или собственный REST API
Горячие клавиши ИспользованиеglobalShortcutНазначьте Ctrl+N для создания новой заметки
Другие расширения