[Перевод] Главный секрет разработки хороших Electron-приложений

Кое-кто люто ненавидит Electron-приложения. То, что приложение включает в себя браузер Chromium, кажется, мягко говоря, странным. Это ощущение усиливается в ходе работы с такими приложениями. Они потребляют много памяти, медленно загружаются и не отличаются особенно высокой скоростью реакции на воздействия пользователя. Непросто разработать хорошее приложение для веба. Зачем же веб-технологии притащили в настольную среду? Ведь возникает такое ощущение, что в этой среде они создают кучу проблем?

Автор материала, перевод которого мы сегодня публикуем, говорит, что не станет выступать в роли защитника Electron, хотя всё говорит об успешности этой платформы. Правда, никто не собирается сбрасывать со счетов излишества, которые присущи Electron-приложениям. Можно ли, разрабатывая такие приложения, убить одним выстрелом двух зайцев?

r0cf5b3kgqsgqfbectxrpwhyxqq.png


Некоторые из проблем Electron (большие размеры файлов, медленная загрузка) являются наследием технологий, на которых основана эта платформа. Их надо решать на низком уровне. Более серьёзные проблемы (потребление памяти, неповоротливость интерфейсов) могут быть решены на том уровне, на котором разрабатывают Electron-приложения. Однако решать эти проблемы непросто. Что если есть некий секрет, зная который можно, в автоматическом режиме, минимизировать эти недостатки?

Если вы любите читать программный код — можете сразу же заглянуть в этот репозиторий. Тут находится проект, который будет рассмотрен в этом материале.

Суть секрета


Секрет разработки качественных Electron-приложений заключается в выполнении основного объёма вычислений на локальной системе, в фоновых процессах. Чем меньше вы полагаетесь на облачные сервисы, и чем больше работы выносите в фоновые процессы, тем сильнее вы сможете ощутить следующие позитивные эффекты:

  • Мгновенная загрузка данных. Пользователям приложения никогда не придётся ждать загрузки данных по сети. Данные загружаются из локального хранилища. Это сразу же даёт приложению ощутимый прирост производительности.
  • Небольшие потребности в кэшировании. Клиентскому приложению не нужно особенно беспокоиться о кэшировании. Это так из-за того, что все необходимые ему данные доступны локально. Для того чтобы веб-приложение смогло бы выйти на достойный уровень производительности, ему обычно нужно загрузить в своё локальное состояние данные внушительного объёма. Это — одна из причин очень большого потребления памяти Electron-приложений.


Тут я не говорю о других преимуществах локального хранения данных, например, о возможности работы без подключения к сети.

Взгляните на то, какой объём памяти потребляет моё Electron-приложение — менеджер личных финансов Actual. Эта программа хранит все свои данные локально. Синхронизация данных между разными устройствами — это необязательная возможность, она не влияет на основной функционал. Полагаю, если учитывать то, что это приложение предназначено для работы с большими объёмами данных, его показатели потребления памяти говорят сами за себя.

074c800cde464ea55565f49b1fe914d4.png


Потребление памяти приложением Actual

Приложение, которое не выполняет каких-либо активных действий, занимает, в целом, 239.1 Мб памяти. Этот показатель может быть и больше, он зависит от того, что именно происходит в приложении, но за базу можно принять именно указанное число. Это — не идеально, но не так уж и плохо. По крайней мере — лучше, чем те 1371 Мб памяти, которые требуются на моём компьютере Slack. Надо сказать, что Slack — это нетипичный пример Electron-приложения, характеризующийся специфическими проблемами. Вокруг Electron из-за Slack поднялась некоторая шумиха. Другие приложения, вроде Notion или Airtable, потребляют примерно 400–600 Мб памяти. А это значит, что моё приложение неплохо выигрывает в этом плане и у них.

Надо сказать, что показатель в 239.1 Мб получен мной до проведения каких-либо оптимизаций. Я планирую переписать некоторые из чрезвычайно важных и требовательных к памяти фрагментов приложения на Rust. Это должно значительно уменьшить потребности приложения в памяти.

Фоновый сервер может оптимизировать собственное потребление памяти, загружая в память только нужные в некий момент времени данные. Лучше всего для хранения данных пользоваться чем-то вроде SQLite. Эта СУБД уже серьёзно оптимизирована для решения подобных задач (серьёзно — просто пользуйтесь SQLite). Кроме того, надо отметить, что перемещение различных вычислений в фоновые процессы позволяет пользовательскому интерфейсу приложения реагировать на воздействия пользователя настолько быстро, насколько это возможно.

Оказалось, что использование в Electron-приложении фонового сервера даёт разработчику интереснейшие возможности. В следующем разделе мы поговорим обо всех тех невероятных вещах, которые можно делать с его использованием.

Даже если ваше приложение серьёзно завязано на облачные данные — вам может понадобиться фоновый процесс в том случае, если вы собираетесь работать с API Node.js. Спокойно взаимодействовать с этими API можно только из фоновых процессов. Собственно говоря, каким бы ни было ваше Electron-приложение, я полагаю, что знакомство с проектом, который мы сейчас рассмотрим, способно дать вам какие-то полезные идеи.

Приложение electron-with-server-example


Я создал приложение electron-with-server-example для того чтобы на его примере показать всё, что нужно настроить для разработки по-настоящему локальных Electron-приложений. Сделал я это в стремлении увлечь программистов созданием подобных проектов. Мне бы хотелось встретить подобный проект в то время, когда я только начинал работать с Electron.

Технические сведения о приложении можно найти в файле README. Вот общий обзор проекта:

  • В нём создаётся обычный процесс Node.js, используемый для выполнения серверного кода в фоновом режиме.
  • В нём, с помощью node-ipc, создаётся IPC-канал, который предназначен для организации прямого взаимодействия между бэкендом и пользовательским интерфейсом приложения.
  • Если проект запускают в режиме разработки, то сервер запускается не в виде фонового процесса. С ним можно взаимодействовать через отдельное окно браузера. Это нужно для целей отладки.


Теперь давайте немного притормозим и приглядимся к последнему пункту этого списка. В режиме разработки с сервером можно взаимодействовать через отдельное окно браузера?

efd5ca15704cbc691bcb39c44be9d5a1.png


Клиентская и серверная части приложения

Да, так оно и есть. После того, как я научился запускать фоновые процессы, я понял, во-первых, то, что в моём распоряжении имеются инструменты разработчика Chromium. А во-вторых — я понял, что я, в отладочных целях, могу пользоваться ими для отладки Node.js-кода. В результате я и говорю о том, что с Node.js можно взаимодействовать через браузер. Это позволяет использовать богатый набор инструментов разработчика браузера, основанного на Chromium, для отладки серверного кода.

А теперь давайте посмотрим на всё то интересное, что мы можем сделать благодаря применению вышеописанной схемы запуска приложения.

Использование консоли


Я добавил в файл server-ipc.js команды логирования запросов и ответов. Исследовать их я могу с помощью консоли браузера.

b4f993738dc46f2302f9d47f4aedcc7f.gif


Отладка Node.js-приложения в консоли браузера

Пошаговое выполнение кода


При отладке серверной части приложения с помощью браузера можно пользоваться пошаговым выполнением кода. Нельзя сказать, что это нечто совершенно фантастическое. Но очень удобно то, что эта возможность всегда под рукой и не требует использования дополнительных программ.

158bdec95849d3ed75613241d479b5a1.gif


Пошаговое выполнение кода

Профилирование


Возможно это — мой любимый инструмент. Речь идёт о блестящем стандартном средстве для исследования производительности кода, которое позволяет профилировать серверную часть приложения.

ed0ad33eb3c6a64ae5c33c43cff36ec5.gif


Исследование производительности серверного кода

С помощью инструментов разработчика браузера можно даже исследовать то, что происходит при запуске фонового процесса (это, скорее всего, самая «тяжёлая» часть запуска приложения).

Для того чтобы это сделать достаточно запустить процесс записи показателей и перезагрузить окно. Перезагрузка приведёт к перезапуску сервера. Это ведёт нас к следующему шагу.

Перезагрузка сервера с помощью комбинации клавиш Cmd+R или Ctrl+R


Ещё одна из возможностей отладки серверного кода в браузере заключается в том, что, так как отладка сервера выполняется в окне браузера, простая перезагрузка содержимого окна приводит к перезапуску сервера! Достаточно воспользоваться комбинацией клавиш Cmd+R (или, для Windows, Ctrl+R), и в вашем распоряжении оказываются самые свежие изменения, внесённые в серверный код. При этом данные фронтенда сохраняются. Это значит, что с клиентской частью приложения можно продолжить работу, но, после перезапуска сервера, клиентская часть уже будет работать с новой версией серверного кода. Это напоминает нечто вроде «горячей» замены кода на работающем сервере.

На следующем рисунке показано, как я, изменив код сервера, перезагрузил страницу, нажав Cmd+R. После этого я продолжил работать с клиентом, который теперь взаимодействует с новой версией сервера.

103c832bd59e4acf8d5d4953d3cfc702.gif


Перезагрузка сервера

Исследование работающей серверной части приложения и «горячая» замена кода


Обычно я, отлаживая сервер, просто добавляю в нужные места кода команды console.log() и перезапускаю его. Но иногда, в особо сложных случаях, бывает так, что крайне полезно было бы заглянуть в то, что происходит в работающем сервере, а не перезагружать его. Возможно, что и не только «заглянуть» внутрь сервера, но и что-то в нём поменять для того чтобы посмотреть на то, как это повлияет на проблему.

В консоли можно пользоваться командой Node.js require. Это означает, что с её помощью можно подключать любые модули сервера и работать с ними в консоли.

Предположим, нам нужно поработать с модулем server-handler.js. Для этого достаточно выполнить в консоли команду let handlers = require('./server-handlers').

Создадим систему для хранения состояния сервера. В нашем случае это будет список всех данных, переданных функции make-factorial (серверное состояние реального приложения будет устроено гораздо сложнее):

handlers._history = []
handlers['make-factorial'] = async ({ num }) => {
  handlers._history.push(num)
  
}


Исследовать состояние сервера можно из консоли, подключив соответствующий модуль и взглянув на handlers._history. При желании, во время выполнения программы, мы можем даже полностью заменить реализацию make-factorial!

004f534a249589d449baae10e1d6963a.gif


Исследование состояния сервера

Итоги


Взгляните на репозиторий electron-with-server-example для того, чтобы почитать о деталях реализации проекта и посмотреть код серверной части Electron-приложения.

Если вы пользуетесь Visual Studio Code, то вы, возможно, привыкли к качественной интеграции инструментов разработчика с Node.js-сервером. При таком подходе вы можете самостоятельно, отдельно от Electron-приложения, запустить сервер. После этого можно сообщить Electron о том, что нужно подключиться к процессу, владельцем которого является VS Code. Однако я предпочитаю использовать существующие инструменты разработки Electron.

Это означает, что у программиста появляется возможность применять любые сторонние инструменты для редактирования кода и при этом иметь полный доступ ко всем инструментам разработчика браузера.

Последние несколько лет я, разрабатывая приложение Actual, постоянно пользуюсь тем, о чём только что рассказал. И хочу сказать, что всё это мне очень нравится. Возможно, работа над Node.js-частью этого приложения стала источником самых приятных из когда-либо испытанных мной впечатлений от программирования.

Кроме того, очень важно то, что вышеописанные принципы помогают нам разрабатывать по-настоящему локальные приложения. Все необходимые для этого технологии у нас уже есть. Главное — правильно ими воспользоваться.

Уважаемые читатели! Как вы относитесь к приложениям, основанным на Electron?

b4fnf52x9i3mn80tttdafqtvkfe.jpeg

© Habrahabr.ru