Прошло несколько дней с тех пор, как я наткнулся на проект по созданию настольного приложения. В прошлом я работал над веб-приложениями и мобильными приложениями, используя React и React-Native, но это было что-то новое. Давным-давно я слышал о фреймворке, который позволяет нам создавать мультиплатформенные настольные приложения на JavaScript, и подумал про себя, что его следует использовать для этого проекта. Этим фреймворком был Электрон!
Приложения Электрон работают в два потока, основной и рендерер. Основной поток использует API-интерфейсы Electron для связи с ОС для выполнения собственных операций настольных приложений. Поток рендеринга отвечает за рендеринг DOM в окне приложения. Эти два потока взаимодействуют друг с другом, используя IPC (межпроцессное взаимодействие) Электронный API.
На домашней странице Electron четко сказано, что это проще, чем вы думаете, и это верно для большинства из них, но установка была не такой уж простой. Я собирался использовать React для проекта, поэтому я начал с использования electron-react-boilerplate, который также упоминался в Документации Electron. Проблема, с которой я столкнулся с этим шаблоном, заключалась в том, что он был слишком громоздким и в нем было установлено много модулей, которые не имели отношения к делу. Мне требовался шаблон, который имел только минимальные зависимости для создания приложения Electron на основе React. После обширных исследований я решил, что должен создать его для себя 😬.
Внедряем программирование:
Я начал с простого стартового кода, который нашел в документации. Он создает окно и загружает в него файл HTML, что довольно просто. Затем загрузите приложение React внутри этого окна. У меня уже был шаблон приложения React, который я создал некоторое время назад. Сначала полегче: я связал приложение React, попытался загрузить выходной HTML-файл в окно, но это не сработало. Я понял, что мне нужно внести некоторые изменения в конфигурацию Webpack. Я использую "/"
вместо publicPath
в выходных данных Webpack, потому что это требуется, когда приложение обслуживается с сервера. В случае с Electron файлы будут браться из локальной системы, поэтому я его удалил. Затем я удалил имена хеш-файлов, удалил оптимизацию разделения фрагментов и использовал одно имя файла, то есть renderer.js
, и удалил полифилл, потому что он не требуется. После всех этих изменений я снова запустил бандл в приложении Electron и ЭВРИКА! это сработало.
Нужен режим разработки
Следующая задача — запустить приложение в режиме разработки с помощью HMR (горячая замена модуля). Для веб-приложения React я использую webpack-dev-server для обслуживания файлов и использования HMR. Чтобы использовать то же самое в Electron, я наткнулся на функцию BrowserWindow.loadURL, которая может загружать URL-адрес внутри окна приложения Electron. Теперь я обслуживал приложение React с помощью сервера разработки, передал https://localhost:3000/ внутри функции loadURL
, и теперь мое приложение работало в среде разработки. Но вскоре я понял, что HMR не работает, и начал искать решение. В electron-react-boilerplate, упомянутом в документации, я заметил, что они также использовали пакет react-hot-loader для HMR. Я использовал то же самое и убедился, что он используется только в сборке разработки с использованием переменных среды, установленных в конфигурации Webpack, и теперь все заработало гладко.
Потоковое общение
Далее, использование API-интерфейсов IPC для связи между потоками. Я попытался импортировать модуль ipcRenderer
в приложение React из пакета electron
, но это вызвало ошибку. По-видимому, electron
— это модуль узла, и любое приложение, использующее его, должно работать в среде узла, но наш renderer.js
на самом деле работает в среде браузера. Изучив эту проблему, я обнаружил, что могу установить флаг nodeIntegration
в параметрах BrowserWindow на true
, чтобы предоставить приложение рендерера со средой узла. Благодаря этому я смог использовать модуль ipcRenderer
, но позже я обнаружил, что приложение рендерера не должно отображаться в среде узла, потому что среда узла предоставляет доступ к файловой системе приложению, и любой внешний внедренный скрипт может получить доступ к файлу. система 🤷🏻♂️.
Я нашел несколько решений, которые не работали. Наконец, я вернулся к громоздкому electron-react-boilerplate ипроверил, как они справились с этой проблемой, и обнаружил, что они передали JS-приложение React в комплекте с предварительной загрузкой BrowserWindow options. На самом деле это дает два преимущества: во-первых, сценарии предварительной загрузки имеют доступ к среде узла, а во-вторых, это ускоряет загрузку приложения React в окне приложения. И снова ЭВРИКА! это работает отлично. Я мог отправлять и получать сообщения в и из основного потока и потока рендерера. Я также создал файл common.js
, где я могу хранить имена каналов связи, используемые как основным потоком, так и потоком рендерера.
Подготовка к производству
Читая больше документации, я узнал, что Webpack предоставляет определенные цели для связывания JS-кода для Electron. Для связывания кода рендерера electron-renderer
и для связывания скрипта предварительной загрузки electron-preload
. Я использую electron-renderer
в режиме разработки, потому что в этом случае я не предварительно загружаю JS своего приложения React и устанавливаю nodeIntegration
в true
для разработки. Для производства я использую electron-preload
и устанавливаю nodeIntegration
на false
, а также обязательно удаляю ссылку на файл renderer.js
в index.html
, что можно сделать в конфигурации Webpack, потому что он будет предварительно загружен. Вот как выглядит моя конфигурация главного окна:
const mainWindow = new BrowserWindow({ show: false, backgroundColor: "#000000", width: 1024, height: 768, webPreferences: { nodeIntegration: isDev, contextIsolation: !isDev, enableRemoteModule: false, preload: isDev ? null : path.join(__dirname, "build/renderer.js"), }, });
Мне также нужно убедиться, что при разработке HTML-код загружается с сервера разработки, работающего на локальном хосте, а при производстве он должен загружаться в виде файла.
const APP_URL = isDev ? "http://localhost:3000" : `file://${__dirname}/build/index.html`; mainWindow.loadURL(APP_URL);
Советы по безопасности
У Электрона есть полная страница со всеми советами по безопасности, которым мы, как разработчики, должны следовать. Я потратил много времени на этот документ, и почти все рекомендации по безопасности были обработаны в шаблоне, который я создал. Существуют надлежащие проверки для включения и отключения некоторых свойств в зависимости от среды, в которой запускается приложение. Вы можете прочитать больше об этих рекомендациях по безопасности здесь: https://www.electronjs.org/docs/tutorial/security.
Упаковка и доставка
Для упаковки и доставки приложения я не смог найти более простой инструмент, чем electron-builder. Этот инструмент может упаковать приложение для всех платформ одной простой командой. Все, что вам нужно, это создать файл electron-builder.yml
, указать некоторые параметры, а затем запустить команду electron-builder build -mwl
, и она создаст установщики приложений для MacOS, Windows и Linux 🙌🏼. Вот файл electron-builder.yml
, который я использую:
productName: "ElectronReact" appId: "com.electron.ElectronReact" files: - "build/" - "main.js" - "common.js"
Установить значок для приложения тоже просто. Вам просто нужно изображение PNG размером 512px x 512px
, назовите его как icon.png
и убедитесь, что оно включено в список files
в файле electron-builder.yml
.
Вывод
Создание этого шаблона приложения с нуля было непростым делом, и, честно говоря, начинать что-то новое никогда не бывает легко. Я надеюсь, что это сэкономит много времени людям, использующим этот шаблон, для начала новых проектов.
Спасибо за чтение этого!