Electron

Исходное название Atom Shell.
Начиная с Electron 28, поддерживается ESM.
Ссылка: https://github.com/Elanis/web-to-desktop-framework-comparison
NW.js (изначально node-webkit) является основным конкурентом Electron. Оба являются решениями для кроссплатформенных настольных приложений, основанными на архитектуре Chromium+Node.js.
NW.js был основан на 2 года раньше Electron, но Electron теперь намного популярнее NW.js, и ситуация вряд ли изменится в будущем.
  • Electron разделяет основной процесс (Main process) и процесс рендеринга (Renderer process).
    Процесс рендеринга делится на два контекста, которые отдельно выполняют код Node.js и клиентский код браузера.
Модель Electron разделяет ответственность, но имеет ли смысл это принудительное разделение ответственности, весьма спорно.
Исключительно громоздкая конструкция IPC и постоянные спектакли с безопасностью значительно ухудшают опыт разработки.
  • NW.js основан на архитектуре Chrome Apps, где Node.js и рендерер Chromium используют один и тот же процесс, что позволяет напрямую использовать модули Node.js на стороне браузера.
    Поскольку нативные плагины Node.js могут быть загружены непосредственно на стороне браузера, производительность NW.js значительно превосходит Electron, который полагается на IPC, и решения на основе WASM.
Существует два режима: разделенный контекст и смешанный контекст:
  • В режиме разделенного контекста существуют две среды: контекст Node и контекст Web. Контекст Node — это среда выполнения для модулей Node.js, а контекст Web — это среда выполнения для скриптов NW.js.
    Из-за изоляции глобальных переменных в разных контекстах легко могут возникнуть проблемы с равенством ссылок.
  • В режиме смешанного контекста используется только один контекст.
    Проблемы с равенством ссылок не возникают, но существует риск загрязнения контекста сторонними библиотеками.
  • Точкой входа Electron является JavaScript-скрипт основного процесса.
  • Точкой входа NW.js является в основном HTML-файл, но также может быть JavaScript-скрипт.
Документация Electron значительно превосходит документацию NW.js. Последняя имеет плохую читаемость и сильно устарела.
  • Electron не имеет встроенной защиты исходного кода; для этого требуются решения на основе байткода V8, такие как bytenode или electron-vite.
  • NW.js имеет официально предоставляемый компилятор кода nwjc. Принцип тот же, что и у bytenode, но реализация немного отличается:
    Поскольку NW.js может напрямую модифицировать V8, это позволяет избежать многих ограничений, присущих внешним решениям, таким как bytenode.
  • Electron имеет много пользователей из коммерческих компаний, самый известный проект — VSCode.
  • Несмотря на более раннее начало, NW.js в настоящее время имеет относительно небольшое количество пользователей.
    Игры, созданные с помощью RPG Maker, вероятно, являются крупнейшим текущим источником пользователей NW.js.
Neutralinojs не включает Chromium в комплект поставки, а вместо этого использует WebView операционной системы.
WebView, как можно догадаться, вызывает проблемы совместимости, поэтому не будет рассматриваться как решение для производственного уровня.
Neutralinojs не поддерживает старые версии Windows: https://github.com/neutralinojs/neutralinojs/issues/486
Tauri использует Rust и WebView операционной системы, не поддерживает модули Node.js, для API требуется писать код на Rust.
WebView, как можно догадаться, вызывает проблемы совместимости, поэтому не будет рассматриваться как решение для производственного уровня.
Помимо настольных платформ, Tauri также планирует поддерживать iOS и Android.
  • Изоляция контекстов создает ненужную сложность.
  • API не хватает единообразия; существует несколько способов удалить меню окна.
  • Слишком быстрая стратегия итерации основных версий.
  • Отсутствие полезных фреймворков для тестирования.
Основной процесс приложения Electron, являющийся точкой входа для всего приложения; в одном экземпляре может быть только один.
Процесс в приложении Electron, отвечающий за пользовательский интерфейс, запускаемый основным процессом; в одном экземпляре может быть несколько.
Процессы рендеринга по умолчанию не имеют прямого доступа к нативным модулям Node.js.
Все модули Node.js должны вызываться косвенно через интерфейсы, изолированные по контексту, созданные скриптами предзагрузки (preload).
Интеграция Node.js для процесса рендеринга может быть включена путем передачи опции nodeIntegration: true при создании окна.
По умолчанию скрипт предзагрузки и хост-среда процесса рендеринга изолированы.
Они могут общаться только через contextBridge и window.postMessage.
Это поведение можно отменить, установив contextIsolation: false.
contextBridge.exposeInMainWorld — единственный способ предоставить API из скрипта предзагрузки хост-среде процесса рендеринга.
Типы данных, доступные для моста, ограничены:
Помимо базовых структурированных клонируемых типов, также допускается отправка функций, Promise, объектов ошибок, но этого все еще недостаточно для удовлетворения распространенных потребностей.
Мост также обладает следующими характеристиками:
  • Для предзагрузки предоставление API является одноразовым; его нельзя повторить, и изменения API не могут быть синхронизированы с хост-средой.
  • Для хост-среды удаленные функции внутри будут отображаться как native code.
  • Предоставленные объекты будут заморожены, поэтому для хост-среды API является только для чтения.
  • Для неподдерживаемых типов могут возникнуть три различных результата: передача undefined /копирование свойств объекта/сообщение об ошибке.
Процесс рендеринга может использовать ipcRenderer для отправки запросов основному процессу.
Этот IPC поддерживает два режима:
  • send: только отправляет сообщения в ipcMain
  • invoke: режим запрос-ответ
Из-за изоляции контекста вызов ipcRenderer.send/invoke в процессе рендеринга очень неудобен.
Основной процесс может отправлять запросы процессу рендеринга, но только при условии, что он сначала получит объект WebContents процесса рендеринга.
Объект WebContents можно получить двумя способами:
  • Рендерер сначала отправляет запрос основному процессу, и event.sender, полученный основным процессом, является объектом WebContents.
  • Основной процесс сохраняет BrowserWindow при создании окна, а затем использует BrowserWindow.webContents.
Из-за изоляции контекста регистрация слушателей событий в процессе рендеринга очень неудобна.
Стандартный интерфейс MessagePort.
https://www.electronjs.org/docs/latest/tutorial/message-ports
Подходит для связи между основным процессом и рендерером, а также между рендерерами.
Поскольку основной процесс и рендерер могут общаться через ipcMain и ipcRenderer,
этот метод связи в основном используется для связи между процессами рендеринга.
Перед установкой связи объекты MessagePort должны быть сначала переданы через ipcMain и ipcRenderer.
С помощью сетевых протоколов можно реализовать IPC, например, HTTP и WebSocket.
Учитывая, насколько громоздко устанавливать связь между основным процессом и рендерером (веб-контекстом) в Electron,
использование IPC на основе сетевых протоколов для снижения сложности также является одним из методов.
  • Интерфейсы IPC будут доступны другим программам в той же сети.
  • Производительность хуже, чем у других методов.
Из-за различий в ABI Electron не может напрямую использовать нативные модули Node.js; модули должны быть скомпилированы специально для Electron.
https://www.electronjs.org/docs/latest/tutorial/devtools-extension
Electron имеет ограниченную поддержку API расширений Chrome и может загружать некоторые инструменты разработки.
В Windows и macOS встроенный API Electron можно использовать для настройки автозапуска:
https://www.electronjs.org/docs/latest/api/app#appsetloginitemsettingssettings-macos-windows
Однако реализация соответствующих функций полна трагических ошибок:
  • https://github.com/electron/electron/issues/32657
  • https://github.com/electron/electron/issues/33308
Эта реализация не поддерживает пользовательские аргументы.
Решение, специально предназначенное для сохранения параметров конфигурации в JSON-файлы.
В основе лежит модуль conf того же автора, поддерживающий JSON Schema и миграцию версий на основе package.json.
Документная база данных, работающая в памяти, с периодическим сохранением на файловую систему.
Разработка неактивная.
Чистый JavaScript-модуль, использующий JSON в качестве базы данных.
Эта штука, по сути, представляет собой просто класс для чтения и записи JSON, без каких-либо выдающихся особенностей.
Автор прекратил поддержку.
Продвинутая база данных, объединяющая RxJS и PouchDB.
Благодаря использованию экосистемы PouchDB, официально поддерживает Electron.
Типичный пример несоответствия количества загрузок и количества звезд на Github; фактическое количество пользователей невелико.
Хотя автор вложил много усилий в этот проект, он все еще имеет крутую кривую обучения для новых пользователей.
Этот проект также зависит от слишком большого количества пакетов, которые автор не может контролировать, что делает проект очень хрупким:
  • RxJS
  • PouchDB
  • pouchdb-find (метод find RxCollection напрямую использует этот пакет, тем самым разделяя те же недостатки)
  • mquery (скопированный код)
Начиная с версии v10, RxDB готовится к отказу от PouchDB.
Независимо от популярности проекта, удобства сопровождения или надежности его зависимостей, RxDB не стоит использовать.
В Node.js PouchDB использует LevelDB в качестве подсистемы хранения через модуль levelup.
Подсистема модуля levelup является нативным модулем, но он нативно поддерживает Electron, поэтому его можно использовать без обработки electron-rebuild.
PouchDB не предназначен для многопроцессных сценариев; он имеет много оптимизаций, основанных на памяти, и его Node.js-подсистема LevelDB также не поддерживает многопроцессность.
Поэтому при использовании в Electron его экземпляры должны создаваться только в основном процессе, а затем предоставляться для доступа рендереру через IPC.
pouchdb-find — это официальное расширение для реализации синтаксиса запросов в стиле Mongo.
Этот пакет имеет множество проблем, включая, помимо прочего:
  • Неправильные результаты даже при наличии индексов:
    https://github.com/pouchdb/pouchdb/issues/7810
    Хотя соответствующая проблема была закрыта, я все еще сталкивался с той же проблемой в пакете v7.2.2.
  • Отбрасывание селекторов:
    https://github.com/pouchdb/pouchdb/issues/6572
    https://github.com/pouchdb/pouchdb/issues/6371
Очевидно, что полагаться на этот пакет для запросов ненадежно.
Без поддержки Mongo PouchDB не имеет практической ценности.
Последняя версия была выпущена в 2020 году, а ошибки за этот период не были исправлены. Многие критические ошибки в проблемах были проигнорированы сопровождающими.
Его определения типов @types/pouchdb также устарели (адаптированы для PouchDB v6.4).
Автор RxDB в примечаниях к выпуску RxDB v10 также считает, что PouchDB плохо поддерживается.
https://github.com/pouchdb/pouchdb/issues/3224
Как нативный модуль, не поддерживающий Node-API (N-API),
better-sqlite3 требует обработки с помощью electron-rebuild перед использованием, что может привести к проблемам с блокировкой зависимостей.
Это модуль SQLite3, поддерживаемый командой Ghost.
Этот модуль был переписан на основе Node-API начиная с версии v5.
Callback-интерфейс также устанавливает this, что делает невозможным использование стрелочных функций.
Чтобы получить Promise-обертку, необходимо установить стороннюю обертку node-sqlite.
Каждый экземпляр Database в этой библиотеке — это просто соединение, без каких-либо условий гонки.
Во время начала транзакции все операторы, выполняемые в этом экземпляре, становятся частью этой транзакции.
Поэтому всегда необходимо создавать новый экземпляр для начала транзакции, а для повторного использования экземпляров требуется создание пула, что усложняет решение.
Это библиотека SQLite3, используемая VSCode; это форк sqlite3.
https://www.electronjs.org/docs/latest/tutorial/application-distribution
По сути, распространение приложений достигается путем распространения предварительно собранных бинарных пакетов Electron, которые соответствуют определенной структуре каталогов.
Например, в Windows ручной метод распространения заключается в загрузке предварительно собранного бинарного пакета Electron для соответствующей версии и платформы,
создании каталога resources/app в каталоге Electron, а затем копировании всех файлов проекта в этот каталог.
После этого обычно требуется переименовать исполняемый файл Electron и изменить его значок.
Официальный пакет electron-packager может использоваться для упрощения описанных выше шагов.
Специальный для Electron формат файлов, похожий на tar, предназначенный для упаковки проектов Electron в один файл.
Файл resources/app.asar может заменить папку resources/app.
Некоторые нативные модули выдают эту ошибку после упаковки в файл asar.
Без упаковки asar Electron может корректно находить и загружать нативные модули.
Использование --asar.unpack=*.{node,dll} вместо --asar в electron-packager может решить эту проблему.
Ссылки:
  • https://github.com/electron/packager/issues/424
  • https://github.com/electron/asar/issues/75
app.isPackaged широко используется для определения, находится ли приложение Electron в среде разработки или в производственной среде.
Однако его метод определения довольно контринтуитивен: он проверяет, является ли имя исполняемого файла Electron все еще electron.
Этот пакет используется для автоматизации создания приложений для различных платформ.
  • Поддерживает установку значков исполняемых файлов.
  • Поддерживает удаление devDependencies для уменьшения размера пакета.
electron-packager не имеет функции игнорирования путей при упаковке с помощью файлов игнорирования, хотя когда-то был PR для файла .electronignore, но он был отклонен
(причины отказа не имели логической и фактической поддержки).
Отсутствие этой функции делает electron-packager бесполезным пакетом, потому что, если не добавить большое количество параметров игнорирования в командную строку, среда разработки будет упакована вместе.
Когда включена опция --all, использование ресурсов ввода/вывода и ЦП этим проектом значительно выше, чем требуется для выполняемых задач.
После проверки исходного кода было обнаружено, что программа просто распараллеливает все задачи, что в Electron v28 означает одновременную загрузку 10 экземпляров Electron.
Учитывая пропускную способность сервера и пропускную способность загружающей стороны, этот безудержный параллелизм лишь увеличивает вероятность возникновения сетевых ошибок.
Открытое решение для установщиков Windows.
Поддерживается electron-builder.
Поддерживается пакетом сообщества electron-wix-msi.
Формат UWP, поддерживаемый с Windows 8, может быть опубликован в Microsoft Store.
electron-builder может использовать инструмент MakeAppx для упаковки программ в appx.
Основной формат, поддерживаемый Microsoft с 2018 года, предназначен для замены MSI, для систем Windows 10 и выше, может быть опубликован в Microsoft Store.
  • В сообществе Electron пока нет решения для упаковки MSIX.
  • Поставщик решений для упаковки Advanced Installer поддерживает упаковку проектов Electron в MSIX.
  • Официальный инструмент Microsoft MSIX Packaging Tool может использоваться для перепаковки установщиков EXE или MSI в MSIX:
    https://docs.microsoft.com/en-us/windows/msix/packaging-tool/create-app-package
У Electron была официально поддерживаемая библиотека тестирования на основе ChromeDriver, Spectron, но она была в конечном итоге устарела в начале 2022 года из-за отсутствия сопровождающих.
Потенциальной альтернативой Spectron является Playwright.
Однако, по состоянию на Playwright v1.16, тестирование на основе Playwright все еще является экспериментальной функцией и в значительной степени недоступно:
  • Невозможно отлаживать ошибки
  • evaluate не поддерживает импорт модулей Node.js
Библиотека для создания наложений окон.
Комплексное решение для разработки и упаковки приложений Electron.
Комплексное решение для упаковки и распространения приложений Electron.
Документация непоследовательна до такой степени, что это кажется жалко.
Electron использует другую версию ABI, чем Node.js, поэтому нативные модули, загруженные npm и yarn для текущей версии Node.js, не могут быть использованы напрямую.
electron-rebuild — это инструмент для решения этой проблемы.
По соображениям удобства сопровождения не рекомендуется использовать нативные модули, требующие electron-rebuild.
  • electron-rebuild не может выбрать правильную версию ABI, что может привести к тому, что модули, зависящие от electron-rebuild, станут труднообновляемой зависимостью.
    https://github.com/electron/electron-rebuild/issues/886
  • Несмотря на то, что electron-rebuild находится в официальном репозитории, он часто не поспевает за последними версиями Electron.