Перейти к содержанию

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

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

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

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

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

Однако он подчеркивает, что простота не означает:

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

Простота или нет — это то, насколько все взаимосвязано.

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

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

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

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

Главный поток:

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

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

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

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

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

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

Разделение задач

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

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

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

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

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

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

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

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

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

Совместимость

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

  • Классифицировать типы работ, выполняемых вашим приложением.
  • Построение для них общих интерфейсов ввода и вывода.

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

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

Учитывая это, можно создать библиотеку композитной функциональности, разбив код на категории и создав общие интерфейсы ввода/вывода для этих категорий. Композитный код является отличительной чертой простых кодовых баз: слабосвязанные, взаимозаменяемые части, которые могут располагаться "рядом" друг с другом и опираться друг на друга, в отличие от сложного кода, который глубоко взаимосвязан и поэтому не может быть легко разделен. А в Интернете композитный код может означать разницу между перегрузкой основного потока и его отсутствием.

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

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

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

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

Существует три вида рабочих.

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

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

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

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

Попробуйте сами

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

Ресурсы

Источник — Complexity management

Комментарии