Управление сложностью

Поддерживать простоту веб-приложения может быть на удивление сложно. В этом модуле вы узнаете, как веб-API работают с потоками и как их можно использовать для распространенных шаблонов PWA, таких как управление состоянием.

Простота и сложность

В своем докладе Simple Made Easy Рич Хики обсуждает качества простых и сложных вещей. Он описывает простые вещи как сосредоточение внимания на:

«Одна роль, одна задача, одна концепция или одно измерение».

Но подчеркивает, что простота заключается не в следующем:

«Наличие одного экземпляра или выполнение одной операции».

Независимо от того, является ли что-то простым, зависит от того, насколько оно взаимосвязано.

Сложность возникает из-за связывания, плетения или, используя термин Рича, соединения вещей воедино. Вы можете рассчитать сложность, посчитав количество ролей, задач, концепций или измерений, которыми что-то управляет.

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

Управление сложностью PWA

Весь JavaScript, который мы пишем для Интернета, в какой-то момент затрагивает основной поток. Однако основной поток изначально имеет много сложностей, которые вы, как разработчик, не можете контролировать.

Основная тема:

  • Отвечает за отрисовку страницы, которая сама по себе представляет собой сложный многоэтапный процесс, включающий расчет стилей, обновление и компоновку слоев, а также рисование на экране.
  • Отвечает за прослушивание событий и реагирование на них, включая такие события, как прокрутка.
  • Отвечает за загрузку и выгрузку страницы.
  • Управление медиа, безопасностью и идентификацией. Это все еще до того, как любой написанный вами код сможет выполняться в этом потоке, например:
  • Манипулирование DOM.
  • Доступ к конфиденциальным API, например возможностям устройства или медиа/безопасности/идентификации.

Как сказал Сурма в своем выступлении на Chrome Dev Summit в 2019 году , основной поток перегружен работой и ему недоплачивают.

И тем не менее, большая часть кода приложения также находится в основном потоке.

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

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

Плохие новости? Усложнение основного потока — почти верный способ затруднить достижение этих целей. Хорошие новости? Потому что то, что должен делать основной поток, ясно: его можно использовать в качестве руководства, которое поможет уменьшить зависимость от него в остальной части вашего приложения.

Разделение интересов

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

  • Непосредственно касается DOM.
  • Использует API, которые затрагивают возможности устройства, например уведомления или доступ к файловой системе.
  • Затрагивает идентификационные данные, например файлы cookie пользователя, локальное хранилище или хранилище сеансов.
  • Управляет мультимедиа, например изображениями, аудио или видео.
  • Имеет последствия для безопасности, которые требуют вмешательства пользователя для одобрения, например API веб-сериала.

Работа, не связанная с пользовательским интерфейсом, может включать в себя такие вещи, как:

  • Чистые расчеты.
  • Доступ к данным (fetch, IndexedDB и т. д.).
  • Крипто.
  • Обмен сообщениями.
  • Создание или манипулирование большими двоичными объектами или потоками.

Работа, не связанная с пользовательским интерфейсом, часто записывается работой пользовательского интерфейса: пользователь нажимает кнопку, которая запускает сетевой запрос к API, который возвращает результаты анализа, которые затем используются для обновления DOM. При написании кода этот сквозной процесс часто учитывается, но то, где живет каждая часть этого потока, обычно не учитывается. Границы между работой с пользовательским интерфейсом и работой, не связанной с пользовательским интерфейсом, так же важно учитывать, как и сквозной опыт, поскольку именно они являются первым местом, где вы можете снизить сложность основного потока.

Сосредоточьтесь на одной задаче

Один из самых простых способов упростить код — разделить функции так, чтобы каждая из них была сосредоточена на одной задаче. Задачи можно определить по границам, выявленным при прохождении сквозного опыта:

  • Сначала ответьте на ввод пользователя. Это работа с пользовательским интерфейсом.
  • Далее сделайте запрос API. Это работа, не связанная с пользовательским интерфейсом.
  • Затем проанализируйте запрос API. Это опять же работа, не связанная с пользовательским интерфейсом.
  • Затем определите изменения в DOM. Это может быть работа пользовательского интерфейса, а если вы используете что-то вроде реализации виртуальной DOM, это может быть не работа пользовательского интерфейса.
  • Наконец, внесите изменения в DOM. Это работа с пользовательским интерфейсом.

Первые четкие границы проходят между работой с пользовательским интерфейсом и работой, не связанной с пользовательским интерфейсом. Затем необходимо принять решение: создание и анализ запроса API — это одна задача или две? Если изменения DOM не связаны с пользовательским интерфейсом, связаны ли они с работой API? В той же теме? В другой теме? Правильный уровень разделения здесь является ключом как к упрощению вашей кодовой базы, так и к возможности успешно перемещать ее части из основного потока.

Компонуемость

Чтобы разбить большие сквозные рабочие процессы на более мелкие части, вам нужно подумать о возможности компоновки вашей кодовой базы. Взяв за основу функциональное программирование, рассмотрим:

  • Категоризация типов работы, которую выполняет ваше приложение.
  • Построение для них общих входных и выходных интерфейсов.

Например, все задачи извлечения API принимают конечную точку API и возвращают массив стандартных объектов, а все функции обработки данных принимают и возвращают массив стандартных объектов.

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

Имея это в виду, вы можете создать библиотеку компонуемых функций, классифицируя свой код и создавая общие интерфейсы ввода-вывода для этих категорий. Составной код — отличительная черта простых кодовых баз: слабосвязанные, взаимозаменяемые части, которые могут располагаться «рядом» друг с другом и дополнять друг друга, в отличие от сложного кода, который глубоко взаимосвязан и поэтому не может быть легко разделен. А в Интернете компонуемый код может означать разницу между перегрузкой основного потока или нет.

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

Использование веб-воркеров для снижения сложности

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

Веб-работники позволяют PWA запускать (некоторые) JavaScript вне основного потока.

Есть три типа работников.

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

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

Например, общий работник может управлять доступом и транзакциями для IndexedDB PWA и транслировать результаты транзакций по всем вызывающим сценариям, чтобы они могли реагировать на изменения.

Последний веб-воркер подробно рассматривается в этом курсе: сервис-воркеры , которые действуют как прокси-сервер для сетевых запросов и используются всеми экземплярами PWA.

Попробуй сам

Пришло время кода! Создайте PWA с нуля, основываясь на всем, что вы узнали в этом модуле.

Ресурсы