Инновации в интерфейсных JavaScript-фреймворках — одно из величайших техно-культурных явлений нашего времени. Уже более 20 лет мы наблюдаем, как разворачивается длинный хвост эволюционного творчества. Каждая новая идея попадает в общий котел, вызывая улучшения как в процессе разработки программного обеспечения, так и в конечных продуктах, которые создают разработчики.
Один из фреймворков, делающих себе имя в наши дни, — это Alpine.js. Alpine — это минималистичный фреймворк, созданный, как следует из его названия, для легкой езды по пересеченной местности. Он обеспечивает большую мощность в скудной, легко осваиваемой упаковке. Эта статья познакомит вас с Alpine.js, чтобы вы могли понять, что он дает и когда он может быть вам полезен.
Минималистичный API Alpine
Как описано в документации Alpine.js, API Alpine представляет собой набор из 15 атрибутов, шести свойств и двух методов. Это очень маленький профиль API. Его минималистская цель — обеспечить реактивность в чистом формате, дополненную некоторыми окружающими тонкостями, такими как события и центральное хранилище.
Рассмотрим очень простую веб-страницу в листинге 1.
Листинг 1. Простая веб-страница, созданная с помощью Alpine.js
<html>
<head>
<script src="https://unpkg.com/alpinejs@3.10.3/dist/cdn.min.js" defer></script>
</head>
<body>
<div x-data="">
<span x-text="'Text literal'"></span>
</div>
</body>
</html>
Помимо включения пакета Alpine через CDN (ознакомиться с defer
здесь семантика), единственные две вещи, связанные с Alpine, — это директивы x-data
и x-text
.
Если вы поместите это на HTML-страницу в своей системе и просмотрите ее в браузере, вы увидите вывод сообщения «Текстовый литерал». Хотя это приложение не очень впечатляет, оно демонстрирует два интересных аспекта Alpine.
Во-первых, чтобы реактивность заработала, вы должны заключить разметку в x-data
директива. Если удалить директиву, x-text
не вступит в силу. По сути, x-data
директива создает компонент Alpine. В этом примере x-data
директива пуста. В реальном использовании у вас почти всегда есть данные; в конце концов, вы пишете компоненты, целью которых является реакция на данные.
Второе, на что следует обратить внимание в листинге 1, это то, что вы можете поместить любой действительный код JavaScript в x-text
. Это относится ко всем директивам Alpine.
Элементы x-data и x-text
x-data
содержимое предоставляется всем содержащимся элементам. Чтобы понять, что я имею в виду, взгляните на листинг 2.
Листинг 2. Взаимодействие x-data и x-text
<div x-data="{ message: 'When in the course of human events...' }">
<span x-text="message"></span>
</div>
Теперь страница выведет начало Декларации независимости. Ты это видишь x-data
определяет простой старый объект JavaScript с одним полем, 'message'
, который содержит преамбулу Декларации. x-text
относится к этому полю объекта.
Реактивность в Alpine.js
Далее мы воспользуемся реактивностью, чтобы исправить ошибку в декларации. Взгляните на листинг 3.
Листинг 3. x-on:click и реактивность
<div x-data="{ pronoun: 'men' }">
<button x-on:click="pronoun = 'people'">Fix It</button>
<span x-text="`all ${pronoun} are created equal`"></span>
</div>
x-text
директива должна быть самоочевидной сейчас. Это относится к pronoun
переменная, выставленная x-data
директива. Новым элементом здесь является кнопка, которая имеет x-on:click
директива. Обработчик этого события щелчка заменяет старое местоимение по умолчанию гендерно-нейтральным, а реактивность заботится об обновлении ссылки в x-text.
Функции в данных
Свойства данных в Alpine — это полнофункциональные объекты JavaScript. Давайте рассмотрим другой способ обработки вышеупомянутого требования, показанный в листинге 4.
Листинг 4. Использование функций данных
<div x-data="{
pronoun: 'men',
fixIt: function(){
this.pronoun = 'people';
}
}">
<button x-on:click="fixIt()">Fix It</button>
<span x-text="`all ${pronoun} are created equal`"></span>
</div>
В листинге 4 вы можете видеть, что объект данных теперь содержит fixIt
метод, который вызывается обработчиком кликов.
Кроме того, обратите внимание, что иногда вы будете видеть код приложения, который вызывается из x-data
директива для функции, определенной в теге script — это личное предпочтение, и она работает точно так же, как встроенная x-data
:
<div x-data="myDataFunction()">...</div>
...
<script>
function myDataFunction() {
return {
foo: "bar"
}
}
</script>
Получение удаленных данных
Теперь давайте переключим передачу и подумаем о более сложном требовании. Допустим, мы хотим загрузить список американских президентов в формате JSON из внешнего API. Первое, что мы собираемся сделать, это загрузить его при загрузке страницы. Для этого мы будем использовать x-init
директива, как показано в листинге 5.
Листинг 5. Предварительная загрузка данных из x-init
<div x-data="{
presidents: []
}"
x-init="(
async () => {
const response = await fetch('https://raw.githubusercontent.com/hitch17/sample-data/master/presidents.json');
presidents = await response.json();
}
)">
<span x-text="presidents"></span>
</div>
Что тут происходит? Ну, во-первых, x-data
директива должна быть ясной: она просто имеет presidents
поле с пустым массивом. x-text
в span
element выводит содержимое этого поля.
x-init
код немного сложнее. Во-первых, обратите внимание, что он обернут в самовыполняющуюся функцию. Это связано с тем, что Alpine ожидает функцию, а не определение функции. (Если бы вы использовали неасинхронную форму обратного вызова fetch
вам не нужно было бы обертывать функцию таким образом.)
Как только мы получили список президентов от конечной точки, мы вставляем его в presidents
переменная, которую Alpine представила как часть x-data
объект.
Повторюсь: Alpine.js получает данные из a-data
доступны для других директивных функций (таких как x-init
) в том же контексте.
Итерация с Alpine.js
На данный момент наше приложение извлекает данные из удаленной конечной точки и сохраняет их в состояние. Обратите внимание, однако, что он выводит что-то вроде [Object],[Object]...
. Это не то, чего мы хотим. Давайте взглянем на перебор данных, как показано в листинге 6.
Листинг 6. Итерация с помощью Alpine.js
<div x-data=...>
<ul>
<template x-for="pres in presidents">
<li><div x-text="pres.president"></div>
From: <span x-text="pres.took_office"></span> Until: <span x-text="pres.left_office"></span></li>
</template>
</ul>
</div>
Листинг 6 содержит обычный неупорядоченный список, за которым следует элемент шаблона HTML, содержащий x-for
директива. Эта директива работает аналогично тому, что вы могли видеть в других реактивных фреймворках. В этом случае это позволяет нам указать коллекцию, presidents
и идентификатор, который будет предоставлен заключенной разметке, представляющей каждый экземпляр этой коллекции, в данном случае pres
.
Остальная часть разметки использует pres
переменная для вывода данных из объектов через x-text
.
Теперь приложение выглядит примерно так, как показано на рисунке 1.
Рисунок 1. Список президентов США.
Показать/скрыть и по клику
Теперь мы хотим настроить приложение таким образом, чтобы данные о президенте переключались щелчком по имени президента. Для начала мы изменим разметку так, как показано в листинге 7.
Листинг 7. Показать/скрыть элементы
<template x-for="pres in presidents">
<li><div x-text="pres.president" x-on:click="pres.show = ! pres.show"></div>
<div x-show="pres.show">
From: <span x-text="pres.took_office"></span> Until: <span x-text="pres.left_office"></span></li>
</div>
</template>
Теперь в листинге 7 мы можем использовать x-show
директива о div
содержащий информацию о президенте. Правдивость x-show
value определяет, видимо ли содержимое. В нашем случае это определяется pres.show
поле. (Обратите внимание, что в реальном приложении вы можете не захотеть использовать фактические бизнес-данные для размещения переменной show/hide.)
Чтобы изменить значение pres.show
мы добавляем x-on:click
обработчик заголовка. Этот обработчик просто меняет истинное/ложное значение pres.show
: pres.show = ! pres.show
.
Добавить анимацию перехода
В Alpine есть встроенные переходы, которые можно применить к функции «показать/скрыть». В листинге 8 показано, как добавить анимацию по умолчанию.
Листинг 8. Добавление перехода для отображения/скрытия
<div x-show="pres.show" x-transition>
From: <span x-text="pres.took_office"></span> Until: <span x-text="pres.left_office"></span></li>
</div>
Единственное, что изменилось, это то, что элемент, несущий x-show
директива теперь также имеет x-transition
директива. По умолчанию Alpine применяет разумные переходы. В этом случае переход представляет собой эффект скольжения и затухания. Вы можете широко настроить переход, в том числе применяя собственные классы CSS к различным этапам анимации. Дополнительные сведения об этой функции см. в документации по переходу Alpine.js.
Привязка к входам
Теперь мы добавим возможность простого фильтра. Для этого потребуется добавить ввод, который вы привязываете к своим данным, а затем отфильтровать возвращенный набор данных на основе этого значения. Вы можете увидеть изменения в листинге 9.
Листинг 9. Фильтрация президентов
<div x-data="{
filter: '',
presidents: [],
getPresidents: function(){
return this.presidents.filter(pres => pres.president.includes(this.filter) )
}
}"
...
<input x-model="filter" />
...
<ul>
<template x-for="pres in getPresidents">
Обратите внимание, что x-data
Объект теперь имеет поле «фильтр». Это двусторонне привязано к элементу ввода через x-model
директива, которая указывает на «filter
.”
Мы изменили шаблон x-for
директива для ссылки на новый getPresidents()
метод, который реализуется на x-data
объект. Этот метод использует стандартный синтаксис JavaScript для фильтрации президентов на основе того, включают ли они текст в поле фильтра.
Заключение
Как и его тезка, Alpine.js — это легкий рюкзак с основным снаряжением, которое поможет вам пройти через горы. Он минимальный, но достаточный.
Фреймворк включает в себя некоторые функции более высокого уровня, в частности, центральное хранилище и систему обработки событий, а также архитектуру плагинов и экосистему.
В целом, Alpine.js удобен в работе. Если у вас есть опыт работы с другими реактивными фреймворками, Alpine должен быть вам достаточно знаком, чтобы вы могли быстро с ним разобраться. Простота объявления компонента и его данных в x-data
директива попахивает гениальностью.
Вы можете задаться вопросом о межкомпонентной связи. Alpine.js избегает явного связывания между компонентами (например, без реквизита «родитель-потомок»). Вместо этого он использует среду браузера (то есть окно) в качестве шины событий через $dispatch
директива. Это соответствует философии Alpine по добавлению достаточного количества функций для расширения того, что уже есть. Это работает хорошо.
Все эти элементы подвергаются испытанию по мере увеличения размера и сложности приложения. Так что это работает с любым стеком, который вы выберете. Alpine.js — заманчивый вариант, когда вы в следующий раз начнете писать код.