![](https://habrastorage.org/getpro/habr/upload_files/0ce/619/c0c/0ce619c0cb952940edc7e44ec9a23ce2.png)
htmx — инструмент для создания сложных и интерактивных веб-приложений на HTML, альтернатива клиентскому рендерингу на Javascript. В этой статье рассказываем, как библиотека помогает переиспользовать элементы на сервере, сократить объем кода на Javascript и отказаться от сборки.
Это адаптированный перевод статьи htmx and HTML Driven Development фронтенд-разработчика Раджасегара Чандрана. Повествование ведется от лица автора оригинала.
HTML как центр вселенной
Интернет в том виде, в котором мы знаем его сегодня, во многом существует благодаря HTML и CSS. Javascript (JS) мог стать связующим звеном между ними и сделать страницы более динамичными и интерактивными. Но история веб-программирования развивалась иначе — после появления клиентского рендеринга и других подобных технологий, использовать JS для создания веб-приложений стало заметно сложнее.
Что такое htmx
htmx — библиотека, которая позволяет создавать современные и мощные пользовательские интерфейсы с простой разметкой. С ее помощью AJAX-запросы, запуск CSS-переходов, вызов WebSocket и событий, отправленных сервером, можно проводить непосредственно из элементов HTML.
Я начинал с фуллстэк-разработки, поэтому использование HTML для создания веб-страниц казалось мне естественным. После перехода во фронтенд я быстро разочаровался в клиентском рендеринге на базе JS — технология создавала слишком много проблем.
После интерфейсных фреймворков (например, Ember, React, Svelte) мне гораздо приятнее работать с HTML и hyperscript в связке с htmx. Он позволяет создавать веб-приложения, ориентированные на серверный рендеринг (SSR), и сильно увеличивает мою продуктивность.
SSR-приложения
Использование htmx мотивирует постепенно отказываться от клиентского рендеринга в пользу серверного. Считается, что SSR — это последнее средство, которое используют только когда необходимо быстро повысить производительность. Однако на серверном рендеринге может строится весь пользовательский интерфейс приложения.
htmx не требует для запуска других пакетов JS и она независима от фреймворков или языков. Поэтому использовать ее можно с любой серверной платформой: например, с Node Express, RAILS, Django, Phoenix, Laravel и другими.
Переиспользование компонентов на сервере
htmx позволяет переиспользовать элементы пользовательского интерфейса на стороне сервера с помощью более привычных библиотек: например, pug для Node или библиотек шаблонов для RAILS и Django. Это помогает сделать HTML сложным и динамичным.
Вот пример демонстрационного веб-приложения Rentals Listing, созданного на Express.js и HTML. Один и тот же паршиал в нем используется и для статистических, и для динамических сценариев:
ul.results
each rental in rentals
li
article.rental
button.image(type="button", _="on click toggle .large then if #view-caption.textContent === 'View Larger' then set #view-caption.textContent to 'View Smaller' else set #view-caption.textContent to 'View Larger'")
img(src=rental.attributes.image, alt='An image of ' + rental.attributes.title)
small#view-caption View Larger
.details
h3
a(href='/rentals/' + rental.id) #{rental.attributes.title}
.detail.owner
span Owner:
| #{rental.attributes.owner}
.detail.type
span Type:
| #{rental.attributes.category}
.detail.location
span Location:
| #{rental.attributes.city}
.detail.bedrooms
span Bedrooms:
| #{rental.attributes.bedrooms}
.map
img(alt='A map of ' + rental.attributes.title, src=rental.mapbox, width="150",height="150")
В листинге домашней страницы для отображения паршиала я использовал include из библиотеки pug:
extends layout
block content
.jumbo
.right
h2 Welcome to Super Rentals!
p We hope you find exactly what you're looking for in a place to stay.
a.button(href="/about") About Us
.rentals
label
span Where would you like to stay?
input.light(type="text", name="search",
hx-post="/search" ,
hx-trigger="keyup changed delay:500ms" ,
hx-target=".results" ,
hx-indicator=".htmx-indicator")
include includes/rental-list.pug
Каждый раз, когда пользователь ищет жилье в аренду на сайте, я использую один и тот же паршиал для заполнения результатов поиска. Результат выглядит так:
app.post('/search', (req, res) => {
const { search } = req.body;
const results = _rentals.data.filter(r => {
const _search = search.toLowerCase();
const _title = r.attributes.title.toLowerCase();
return _title.includes(_search);
});
const template = pug.compileFile('views/includes/rental-list.pug');
const markup = template({ rentals: results });
res.send(markup);
});
Маршрутизация на стороне сервера
Маршрутизация на стороне клиента создает целый набор проблем. Например, всегда существует дилемма между маршрутизацией на основе хэша и URL. Поскольку history api не поддерживается в старых браузерах (таких как Internet Explorer 11), предпочтение почти всегда отдается маршрутизации на основе хэшей, которая использует идентификаторы фрагментов в URL-адресе.
Большинство фреймворков JS реализуют собственную логику маршрутизации на стороне клиента. При этом под всеми фреймворками используется собственный API браузера: таких как window.history. Это приводит к появлению большого количества шаблонного кода в приложении.
Меньше кода на JS
На мой взгляд, главное преимущество htmx — количество кода на JS, который мы пишем и отправляем в браузер. Вместе с hyperscript библиотека позволяет создавать многофункциональные интерактивные приложения без использования клиентского кода на JS:
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
Когда одностраничные приложения только начали входить в моду, сообщество приняло JSON в качестве стандарта обмена для данными. Теперь, чтобы реконструировать HTML из данных JSON, часто требуется обработать большой объем данных на стороне клиента, которые приходят с сервера через API. В ответах API часто содержатся либо неполные, либо избыточные данные.
![](https://habrastorage.org/getpro/habr/upload_files/bf8/142/36b/bf814236b828b221ace46fd86bfbb557.jpg)
Для решения этой проблемы разработаны сложные альтернативы вроде GraphQL, благодаря которым можно получать от сервера только нужные данные. htmx решает проблему лучше: достаточно заменить HTML на HTML-ответ, полученный от сервера. Больше никаких данных на стороне клиента.
Отсутствие сборки/компиляции
Еще одно преимущество htmx — отсутствие инструментов сборки веб-приложений. Вы можете использовать инструмент через CDN:
<!-- Load from unpkg -->
<script src="<https://unpkg.com/htmx.org@1.3.3>"></script>
Отсутствие сборки — глобальный тренд в разработке веб-приложений. С одной стороны, спецификации модулей ES приняли все разработчики браузеров, с другой — появились инструменты Skypack , Snowpack , Vite, которые сочетаются с подходами CDN и ESM. Все это в конечном счете приведет к тому, что мы будем видеть сборку для Javascript на стороне клиента все реже и реже. Добавьте к этому отсутствие необходимости устанавливать тысячи пакетов npm и поддерживать сложные конфигурации сборки.
Единая база кода
Две базы кода для одного приложения — дополнительные проблемы при разработке. В частности, нужно синхронизировать развертывания обновлений, дважды настраивать конвейер сборки, обновлять фреймворк в обеих базах, поддерживать код и запускать тестовые пакеты из двух источников.
htmx позволяет объединить весь код в одном месте: поскольку рендеринг происходит на стороне сервера, отдельная база для интерфейса не нужна. В долгосрочной перспективе это поможет сэкономить много времени и денег. Кроме того, разработчики смогут действовать более согласованно — им не придется проверять два и более репозиториев.
Принцип локальности поведения (LoB)
Принцип LoB сформулировал теоретик программирования Ричард Гэбриэлл. Он гласит, что все разработчики должны стремиться к тому, что поведение каждого элемента кода должно быть очевидным при его проверке.
Локальность, по словам Гэбриэлла, — главное условие для простоты поддержки кода. Локальность — это характеристика, которая позволяет программисту понять, к какой части архитектуры принадлежит код, увидев лишь небольшую его часть.
Выглядит это так:
The behaviour of a code unit should be as obvious as possible by looking only at that unit of code
<div hx-get="/clicked">Click Me</div>
LoB часто конфликтует с другими принципами разработки ПО: например, с разделением ответственности. Частично этот конфликт был разрешен, когда React представил HTML и CSS в Javascript. Создатель React Пит Хант при этом считает, что речь идет о разделении технологий:
Отсуствие проблем с синхронизацией состояний
Управление состоянием на стороне клиента создает больше проблем, чем решает. Внедрение этого принципа приводит к тому, что управлять состоянием нужно и на стороне клиента, и на стороне сервера. Альтернативное решение — хранить состояние на сервере. В этом случае клиент служит фиктивным исполнителем для рендеринга изменения состояний.
![](https://habrastorage.org/getpro/habr/upload_files/7ef/f63/6a4/7eff636a4c604925f21e4e3f6fdcd677.jpg)
Это похоже на модель тонкого клиента — недокомпьютера с легкой операционной системой, который соединяется с терминальным сервером. Такие устройства использовали для создания первый веб-приложений ради экономии ресурсов.
htmx поможет избежать запутывания кода пользовательского интерфейса в сети управления состоянием: например, в двусторонней привязке данных, однонаправленном потоке данных, реактивных данные.