Современная ситуация с разработкой веб-приложений
Сегодня пользователи ожидают от веб-приложений плавной работы без перезагрузок страниц. К сожалению, эти ожидания обычно реализуются в виде одностраничных приложений (single-page application, SPA), использующих библиотеки и фреймворки наподобие React и Angular. Эти фреймворки очень специализированы и с ними может быть трудно работать.
Новый подход заключается в том, чтобы вернуть возможность реализации этого UX в руки инженеров, разрабатывавших веб-сайты до возникновения безумия SPA, используя готовые наборы инструментов и знания. HTMX — лучший пример такого подхода из тех, что я видел.
Цена SPA
SPA позволяют инженерам создавать отличные веб-приложения, но за это приходится платить свою цену:
- Сильный рост сложности с точки зрения архитектуры и процесса разработки. Нужно много времени тратить на изучение фреймворков.
- Инструментарий для сборки и упаковки кода постоянно меняется.
- Управление состоянием и в клиенте, и на сервере.
- Фреймворки поверх библиотек поверх других библиотек поверх полифилов. Разработчики React даже рекомендуют использовать поверх их технологии фреймворк:
React — это библиотека. Она позволяет соединять компоненты друг с другом, но не определяет способы маршрутизации и получения данных. Для сборки целого приложения на React мы рекомендуем фулстек-фреймворк React.
- По своей природе толстый клиент требует исполнения большого объёма JavaScript. Если вы работаете с современным оборудованием, то это приемлемо, но этими приложениями невозможно будет пользоваться на старом оборудовании или в местах с ненадёжным подключением к Интернету.
- Очень легко реализовать SPA некорректно, поэтому необходимо использовать правильный подход с хуками, чтобы производительность на стороне клиента не была ужасной.
- Некоторые реализации SPA отказываются от progressive enhancement (примечательным исключением является Remix). Поэтому для большинства SPA обязательно нужно включить JavaScript.
- Если вы хотите использовать не JavaScript или TypeScript, то придётся пойти по опасному пути транспиляции.
- Во многих компаниях это привело к созданию больших отделов бэкенда и фронтента, несущих за собой высокие затраты на координацию.
До появления SPA разработчики выбирали удобный им язык и передавали HTML в браузер пользователя в ответ на HTTP-запросы. Это нормально, но обеспечивает низкий уровень интерактивности, а в некоторых случаях создаёт раздражающий UI, особенно из-за того, что при каждом взаимодействии страница полностью перезагружается. Чтобы обойти эту проблему, разработчики обычно добавляли JS по своему вкусу.
Хотя такой подход кому-то кажется устаревшим, именно им вдохновлялись разработчики статьи о REST, особенно что касается гипермедиа. Гипермедийный подход к созданию веб-сайтов привёл к невероятному успеху WWW.
▍ Гипермедиа?
Ниже показан ответ от API данных, не гипермедиа.
{
"sort": "12-34-56",
"number": "87654321",
"balance": "123.45"
}
Чтобы сделать эти данные полезными в SPA, клиентский код должен понимать структуру и решить, что рендерить, и какие элементы управления сделать доступными.
REST описывает использование гипермедиа. Гипермедиа — это когда твои ответы не просто являются сырыми данными, а полезной нагрузкой, описывающей медиа (вспомните тэги HTML наподобие
<p>
, заголовки и так далее) и способ манипуляций с ними (например, form
, input
).Пример гипермедиа — это сервер, возвращающий HTML с описанием банковского счёта с какими-нибудь видами элементов управления для работы с ресурсом. Теперь сервер отвечает за описание способа рендеринга данных (с небольшим вмешательством CSS) и того, какие элементы управления должны отображаться.
<dl>
<dt>Sort</dt><dd>12-34-56</dd>
<dt>Number</dt><dd>87654321</dd>
<dt>Balance</dt><dd>£123.45</dd>
</dl>
<form method="POST" action="/transfer-funds">
<label>Amount <input type="text" /></label>
<!-- etc -->
<input type="submit" value="Do transfer" />
</form>
Такой подход означает, что у нас есть один универсальный клиент — веб-браузер; он понимает, как отображать гипермедиа-ответы и позволяет пользователю работать с «элементами управления» для выполнения нужных действий.
Карсон Гросс в подкасте Go Time:
… когда только появились браузеры, идея одного универсального сетевого клиента, общающегося с любым приложением через эту безумную технологию гипермедиа, была абсолютно новаторской. Такой она и остаётся.
Если бы вы сказали кому-нибудь в 1980 году «знаешь, ты сможешь использовать одно и то же приложение для доступа к новостям, банковскому счёту, календарю, штуке под названием „электронная почта“ и многому другому», то на тебя бы посмотрели искоса и не поняли, о чём ты говоришь, если собеседник не был членом одной из мелких исследовательских групп, изучавших подобные вещи.
До появления World Wide Web, до веб-браузеров превалирующими паттернами приложений были уникальные и часто «толстые» клиенты.
Хотя создающие SPA люди говорят об использовании «RESTful» API для реализации обмена данными в их коде на стороне клиента, в своей чистейшей форме такой подход не является RESTful, потому что в нём не применяется гипермедиа.
Вместо одного универсального клиента куча разработчиков создаёт уникальные клиенты, которые должны понимать, какие сырые данные они получают от веб-серверов, а затем рендерить элементы управления согласно этим данным. При таком подходе браузер больше походит на среду исполнения JavaScript, HTML и CSS.
Толстый клиент по определению требует больше усилий и затрат, чем тонкий. Однако «первоначальный» подход с гипермедиа недостаточно хорош для всех современных нужд; элементы управления, с которыми может работать браузер, и то, что он требует полного обновления страницы для их использования, делают такой UX неудобным для многих типов веб-приложений.
▍ HTMX и гипермедиа
В отличие от SPA, HTMX не отказывается от архитектурного подхода REST; он дополняет браузер, расширяя его гипермедиа-возможности и упрощая реализацию функционального клиента без особо большого объёма JavaScript, а то и совсем без него.
Для передачи HTML можно использовать любой язык программирования, как мы и привыкли. Это значит, что мы можем использовать проверенный временем надёжный инструментарий с «истинным подходом RESTful», что существенно упрощает разработку и снижает сложность.
HTMX позволяет проектировать страницы, получающие с сервера фрагменты HTML для обновления страницы пользователя и без необходимости перезагрузки всей страницы.
Мы можем рассмотреть это на практике при помощи примера с классическим приложением TODO.
Clojure HTMX TODO
Во-первых, не стоит волноваться о том, что приложение пишется на Clojure. Я выбрал Clojure ради интереса, но красота такого подхода в том, что можно использовать любой подходящий вам язык, если он отвечает на HTTP-запросы отправкой гипермедиа.
Здесь нет ничего особенного, но приложение ощущается как SPA. Нет никаких полных перезагрузок страницы, всё работает очень плавно, как во всех демо SPA, которые вы могли видеть.
Разница заключается в следующем:
- Я не писал ни строки на JavaScript.
- Я не жульничал, транспилировав Clojure в JavaScript. (См. ClojureScript)
Я создал веб-сервер, отвечающий на HTTP-запросы отправкой гипермедиа.
HTMX добавляет возможность задания расширенного гипермедиа, позволяя аннотировать любой HTML-элемент, чтобы просить браузер делать HTTP-запросы для получения фрагментов HTML, которые помещаются на страницу.
▍ Функция редактирования
Самая удивительная и впечатляющая часть этого демо — функция редактирования. Поле ввода появляется мгновенно, пользователь может отредактировать и страница сразу же обновится. Кажется, как будто для этого требуется много ванильного JS или подход в стиле React, но ниже вы увидите, что всё до абсурда просто.
Давайте начнём с изучения разметки элемента TODO. Ради понятности я вырезал всю разметку, не относящуюся к редактированию.
<li hx-target="closest li">
<form action="/todos/2a5e549c-c07e-4ed5-b7d4-731318987e05"
method="GET">
<button
hx-get="/todos/2a5e549c-c07e-4ed5-b7d4-731318987e05"
hx-swap="outerHTML">
📝</button>
</form>
</li>
Возможно, вам покажется, что тут много кода, но на самом деле в первую очередь нужно сосредоточиться на понимании функции редактирования:
- Атрибут
hx-target
тега<li>
говорит браузеру: «Когда ты получишь фрагмент для рендеринга, я хочу, чтобы ты заменил этот элемент». Дочерние элементы наследуют этот атрибут, поэтому для любых действий HTMX внутри этого тега<li>
возвращаемыйHTML
будет заменять содержимое<li>
. hx-get
на кнопке редактирования означает, что при нажатии на неёHTMX
прикажет браузеру выполнитьHTTP GET
кURL
и получить новую разметку для рендеринга в<li>
вместо того, что там находится.- Форма в этом примере необязательна, однако она позволяет нам поддерживать функциональность для пользователей с отключенным JavaScript, о которых мы поговорим ниже.
Когда начинаешь работать с HTMX, проще всего разобраться в происходящем, изучив вкладку Network в инструментах разработчика в браузере.
Когда пользователь нажимает на кнопку редактирования, браузер выполняет
HTTP GET
к указанному ресурсу todo. Сервер возвращает гипермедиа-ответ, который является описанием этого ресурса с элементами управления гипермедиа.<form action="/todos/45850279-bf54-4e2e-a95c-c8c25866a744/edit"
hx-patch="/todos/45850279-bf54-4e2e-a95c-c8c25866a744"
hx-swap="outerHTML" method="POST">
<input name="done" type="hidden" value="false"/>
<input name="name" type="text" value="Learn Rust"/>
<input type="submit"/>
</form>
Затем HTMX берёт этот HTML и заменяет им то, что мы определили как
hx-target
. Поэтому пользователь теперь видит эти элементы управления гипермедиа, позволяющие ему манипулировать ресурсом, а не строку, которую мы видели на изображении выше.Можно заметить, что форма имеет атрибут
hx-patch
, то есть при её отправке браузер отправит PATCH
с данными для обновления ресурса. Затем сервер отвечает обновлённым элементом для рендеринга.Применение в вебе
В HTMX есть гораздо больше всего, но это сама суть подхода, который полностью идентичен подходу, который использовался на веб-сайтах до того, как стали популярны SPA.
- Пользователь переходит на
URL
- Сервер возвращает гипермедиа (HTML), то есть контент с элементами управления.
- Браузер рендерит гипермедиа
- Пользователи могут использовать элементы управления для выполнения работы, что приводит к отправке HTTP-запроса из браузера на сервер.
- Сервер выполняет бизнес-логику, а затем возвращает новое гипермедиа, с которым может работать пользователь.
Всё, что делает HTMX — это позволяет браузеру лучше справляться с гипермедиа, давая нам больше вариантов того, что может привести к исполнению HTTP-запроса и позволяя нам обновлять часть страницы, а не перезагружать её полностью.
Благодаря использованию гипермедиа и тому, что мы не относимся к браузеру просто как к среде исполнения JavaScript, мы получаем множество упрощающих жизнь преимуществ:
- На стороне сервера можно использовать любой язык программирования.
- Нам не требуется куча библиотек и другого хлама для поддержки базовых возможностей веб-разработки.
- Кэширования
- Дружелюбности к SEO
- Ожидаемой работы кнопки «назад»
- И так далее
- Очень легко поддерживать пользователей, которые не могут или не желают использовать JavaScript
Последний пункт критически важен для меня и моего работодателя. Я работаю в компании, создающей продукты, используемые по всему миру, и наш контент и инструменты должны подходить для максимального количества людей. Мы не можем ограничивать возможности людей неудачными техническими решениями.
Именно поэтому мы пользуемся progressive enhancement.
Progressive enhancement — это философия проектирования, предоставляющая обязательные контент и функциональность максимально возможному количеству пользователей, а максимальную функциональность давая только тем пользователям, которые используют современные браузеры, способные выполнять весь нужный код.
Все функции приложения TODO (поиск, добавление, редактирование, удаление, пометка пункта как завершённого) работают с отключенным JavaScript. HTMX не делает всего этого «бесплатно», усилия разработчиков всё равно требуются, но благодаря такому подходу задачи решаются гораздо проще. Чтобы написать приложение, мне понадобилось около часа, и при этом не потребовалось существенных изменений.
▍ Как эта система поддерживает отсутствие JavaScript
Когда браузер отправляет запрос, заданный HTMX, он добавляет заголовок
HX-Request: true
; для сервера это означает, что мы можем отправлять разные ответы; это во многом похоже на content negotiation.Стандартная структура для обработчика выглядит примерно так:
parseAndValidateRequest()
myBusinessLogic()
если запрос в htmx то
вернуть фрагмент гипермедиа
иначе
вернуть полную страницу
конец
Вот конкретный пример HTTP-обработчика для работы с новым TODO:
(defn handle-new-todo [get-todos, add-todo]
(fn [req] (let [new-todo (-> req :params :todo-name)]
(add-todo new-todo)
(htmx-or-vanilla req
(view/todos-fragment (get-todos))
(redirect "/todos")))))
Третья строка «бизнес-логики» вызывает функцию для добавления нового TODO в наш список.
Четвёртая строка — это некий код для определения того, с каким типом запроса мы имеем дело, а последующие строки или рендерят фрагмент для возврата, или перенаправляют на страницу.
По моему опыту разработки гипермедия-приложений с HTMX, такой паттерн встречается очень часто. По самой природе такой архитектуры, если мы можем поддерживать обновление части страницы, то возвращаем фрагмент; в противном случае браузер обязан выполнить полное обновление страницы, то есть или выполнить перенаправление, или просто вернуть весь HTML целиком.
Шаблонизация HTML на сервере — это невероятно развитая область. Существует множество вариантов и превосходных руководств по тому, как структурировать и добавлять для них автоматизированные тесты. Важно то, что все они предоставляют возможности композитинга, поэтому можно чрезвычайно просто вернуть фрагмент или целую страницу.
Почему же это будущее?
Конечно, я не могу предсказать будущее, но я считаю, что HTMX (или нечто подобное) в ближайшем будущем станет всё более популярным подходом к созданию веб-приложений.
Недавно было заявлено о том, что HTMX стал одним из двадцати проектов в GitHub Accelerator.
▍ Он делает «фронтенд» более доступным
Изучение React само по себе является целой отраслью знаний. Он быстро развивается и меняется, и нужно многому учиться. Я симпатизирую разработчикам, которые когда-то делали полнофункциональные приложения и которых современная фронтенд-разработка заставила стать «бэкенд»-разработчиками.
Я создавал на React довольно сложные системы, и хотя в чём-то это было довольно интересно, объём необходимых знаний для большинства приложений несопоставим. У React есть своя ниша, но для многих веб-приложений он чрезмерно мощен.
Подход с гипермедиа на основе HTMX несложно понять, особенно если ты знаком с фундаментом REST (который у «бэкенд»-разработчиков должен быть). Он позволяет создавать функциональные веб-сайты более широкой аудитории разработчиков, не желающих изучать работу с фреймворком и постоянно гнаться за его постоянно меняющейся структурой.
▍ Меньше возни
Даже спустя более десятка лет после появления React он не кажется устоявшимся и зрелым. Несколько лет назад хуки стали новинкой, которой должен был научиться каждый и переписать на них все компоненты. А в последние полгода в моей ленте Twitter куча споров и туториалов о новых "RSC" — react server component. Неописуемое удовольствие.
Работа с HTMX позволила мне использовать то, что я изучил 15-20 лет назад и что до сих пор работает, как мой сайт. Этот подход понятен и хорошо задокументирован, а накопленный опыт не зависит от языков программирования и фреймворков.
Я без малейших проблем создал пример приложения на Go и на Clojure, а ведь в Clojure я совершенный новичок. Как только вы разберётесь с основным синтаксисом языка и узнаете, как отвечать на HTTP-запросы отправкой гипермедиа, то сможете приступить к работе; и можно использовать рекомендации по архитектуре и дизайну без необходимости снова и снова изучать новые подходы.
Какой объём навыков вам удастся перенести из React на работу с Angular? Легко ли перейти с одного фреймворка react на другой? Что вы подумаете, когда классовые компоненты станут «плохими» и от вас потребуют использовать вместо них хуки?
▍ Дешевле
Он просто требует меньше усилий!
Hotwire — это библиотека, имеющая схожие с HTMX цели, развитием которой занимается сообщество Ruby on Rails. DHH твитнул следующее.
Hotwiring Rails выражает желание подарить одинокому фулстек-разработчику все инструменты, которые необходимы ему для создания нового Basecamp, GitHub или Shopify. Того, что может создать команда из десятков или сотен людей при наличии миллионов долларов венчурного капитала. Технологии Возрождения для людей эпохи Возрождения.
Поэтому так печально слышать, когда «фулстек» считают уничижительным термином или невыполнимой миссией. Считается, что для создания крутых проектов мы ДОЛЖНЫ быть разрозненным коллективом из фронтенда, бэкенда, сервисов и любых других групп специалистов. А это совершенно не так.
Без когнитивной перегрузки обучения огромному фреймворку из мира SPA и сложностей создания толстого клиента вполне можно создавать функциональные веб-приложения усилиями гораздо меньшего количества разработчиков.
▍ Повышенная надёжность
Как говорилось ранее, при использовании подхода с гипермедиа создавать веб-приложения, работающие без JavaScript, достаточно просто.
Важно помнить, что браузер находится в ненадёжном окружении, поэтому при создании SPA необходимо обеспечивать невероятный уровень защиты. Приходится реализовывать большую часть бизнес-логики на стороне клиента; но из-за архитектуры ту же самую логику необходимо воссоздать и на сервере.
Допустим, нам нужно правило, гласящее, что после того, как список помечен выполненным, его нельзя было редактировать. В мире SPA мы бы взяли сырой JSON и вынуждены были создавать бизнес-логику, определяющую, нужно ли рендерить кнопку редактирования в клиентском коде. Однако, если бы мы хотели, чтобы пользователь каким-то образом не обошёл это правило, ту же защиту нужно было реализовать на сервере. Это кажется несерьёзным и простым, но повышает сложность, а вероятность несогласованности повышается.
При подходе с гипермедиа браузер «туп» и ему не нужно об этом волноваться. Разработчик может реализовать это правило в одном месте — на сервере.
▍ Снижение сложности координации
Сложность SPA привела к созданию больших отделов бэкенда и фронтенда, что привносит дополнительные затраты.
Типичное разделение команды на фронтенд и бэкенд приводит к большой неэффективности командной работы из-за передачи ответственности и недопонимания, что усложняет выполнение работы. Многие ошибочно считают самой критически важной метрикой личную эффективность, используя её как оправдание такому разделению. Они видят, как сливаются вместе множество PR, и прикладывается много усилий, но игнорируют затраты на координацию.
Например, предположим, что вам нужно добавить на страницу новый элемент данных или новую кнопку. Для многих команд это значит необходимость проведения совещаний по согласованию нового API, создание заглушек для команды фронтенда и координирование релизов.
При подходе с гипермедиа эта сложность полностью отсутствует. Если вам нужно добавить на страницу кнопку, координировать усилия не нужно. Не требуется сильно беспокоиться о структуре API. Вы можете менять разметку и контент так, как вам это нужно.
Команды, обменивающиеся данными в JSON, могут быть крайне неустойчивыми без поддержки и всегда требуют затрат на координацию. Вам могут помочь такие инструменты, как consumer-driven contract, но это всего лишь ещё один инструмент, ещё один аспект, в котором нужно разбираться, и ещё один элемент, который может сломаться. При использовании гипермедиа лишняя трата ресурсов, поддержка и сложность управления версиями API по природе своей не является проблемой; вы можете менять разметку, как вам нужно.
При этом я не хочу сказать, что при таком подходе нет места для разделения ролей. Я работал в командах, где разработчики создавали веб-приложение «от и до», но при этом у нас были люди, специализирующиеся на семантической, доступной разметке, помогавшие нам обеспечивать хорошее качество результата. Невероятно удобно, когда для создания веб-сайта вам не приходится обсуждать API и передавать задачи.
▍ Больше возможностей
Рендеринг HTML на сервере — это хорошо исследованный путь. В любом популярном языке программирования и в большинстве нишевых существует множество проверенных практикой и совершенных инструментов и библиотек для генерации HTML на сервере.
Подведём итог
Я призываю разработчиков, стремящихся к снижению затрат и сложности разработки веб-приложений, попробовать HTMX. Если вы отказывались от создания веб-сайтов из-за справедливого мнения о сложности фронтенд-разработки, то HTMX может стать для вас отличным вариантом.
Я не заявляю, что SPA стали излишними; в них по-прежнему существует реальная потребность, когда требуются очень сложные и быстрые взаимодействия, когда требования к проекту не позволяют тратить время на обращение к серверу для получения разметки.
В 2018 году я заявил, что большое количество веб-приложений можно написать с гораздо более простым технологическим подходом, чем SPA. Теперь благодаря технологиям наподобие HTMX моё заявление имеет ещё больший вес. В мире фронтенда разработчики постоянно ждут, что новый фреймворк решит проблемы предыдущего фреймворка, который они используют. Подход с SPA по природе своей более сложен, чем подход с гипермедиа и если наслоение ещё большего количества технологий не может решить проблему, то попробуйте гипермедиа.
Дополнительную информацию можно изучить по ссылкам ниже.
Дополнительное чтение и источники
- Автор HTMX написал превосходную бесплатную книгу с описанием гипермедиа. Её легко читать; возможно, она заставит вас подвергнуть сомнениям свои мысли о том, как правильно создавать веб-приложения. Если вы всегда создавали только SPA, то она обязательна к прочтению.
- HTMX. В частности, раздел с примерами очень хорошо показывает возможности технологии. Эссе тоже написаны качественно.
- Мне повезло быть приглашённым на подкаст GoTime с создателем HTMX Карсоном Гроссом! Хотя этот подкаст посвящён Go, основная часть беседы касалась гипермедиа.
- Версия на Go стала моим приключением в мире HTMX; я создал то самое приложение со списком todo, которое описано в этом посте
- Мы с моим коллегой Ники работали над версией на Clojure
- DHH о Hotwire
- Progressive enhancement
- Пять лет назад я написал статью The Web I Want, в которой жаловался на разрастающиеся затраты на SPA. Первоначальной причиной её написания стало наблюдение за тем, как двухлетний ChromeBook моей подруги тормозил на популярном веб-сайте, который вполне мог быть статичным HTML. В статье я говорю о том, что мне бы хотелось, чтобы больше веб-сайтов придерживалось подхода с гипермедиа, рендерило HTML на сервере и использовало progressive enhancement для повышения удобства. Перечитывая эту статью, я очень порадовался появлению HTMX и подобных ему технологий.
Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх 🕹️