Бібліотека jQuery давно втратила популярність серед розробників, вони не використовують її для нових проєктів і на це є багато причин. Звісно в легасі інтернеті jQuery є і буде, але що тоді робити з новими сторінками? Є альтернатива! І тут я вам трохи розповім про AlpineJS і на що ця бібліотека здатна. Разом з демо і прикладами коду.

AlpineJS. Проста. Легка. Заряджена до зубів.

Хоч розробники і називають AlpineJS фреймворком, я ж буду свідомо використовувати “бібліотека”, щоб не відлякувати потенційних користувачів. За спиною у бібліотеки стоїть дуже проста ідея, дати можливість вписати мінімум скомпонованого коду прямо у розмітку HTML, щоб додати сторінкам динаміки не використовуючи такі бібліотеки як React, чи Solid заради форми на лендінг сторінці.

Загалом AlpineJS налічує 15 атрибутів, 6 властивостей та два методи, чого вистачає для будь яких потреб пересічної сторінки, а головне це все важить ~15кб, а якщо ви читали мої дописи, то знаєте, що для найкращого досвіду користування сайтом сторінка повинна важити менше 14кб.

А сайт бібліотеки каже, щоб ми думали про неї, як про jQuery для сучасного вебу і хто ми такі, щоб цьому перечити ;)

Що можна написати з AlpineJS на прикладі з поясненнями

Хотілось показати вам щось просте для розуміння, але в той же час щось що буде використовувати по максимуму типовий функціонал, яки є на кожному сайті. Презентую вам форму з випадковими картинками.

Це звичайний script тег, який розміщується в head сторінки. Так само цей код можна лишити прямо у HTML, або винести в окремий модуль, за потреби.

<script>
  async function fetchRandomPicUrl(size) {
    const res = await fetch(
      "https://random.imagecdn.app/v1/image?width=" + size + "&height=" + size
    );

    const url = await res.text();

    return url;
  }
</script>

Безпосередньо сама розмітка, яка включає в себе динамічний функціонал за допомоги AlpineJS. Далі по порядку про кожен використаний елемент з бібліотеки.

<div
    x-data="{ url: null, size: 500 }"
    x-init="url = await fetchRandomPicUrl(size)"
    class="flex-center"
  >
    <template x-if="url == null">
      <div class="flex-center image-placeholder">Завантажую...</div>
    </template>
    <template x-if="url">
      <div class="flex-center">
        <div class="header">
          <label>
            Розмір зображення:
            <input required min="100" max="1000" type="number" x-model="size" />
          </label>
          <button
            @click="url = await fetchRandomPicUrl(size)"
            :disabled="size < 100 || size > 1000 ? true : false"
          >
            Застосувати
          </button>
        </div>
        <figure>
          <img x-bind:src="url" alt="random picture" />
          <figcaption class="flex-center">
            <button @click="url = await fetchRandomPicUrl(size)">
              Нове зображення
            </button>
          </figcaption>
        </figure>
      </div>
    </template>
  </div>

x-data

По суті x-data описує компонент в AlpineJS і створює набір початкових даних – стан (state), до яких можна отримати доступ всередині компонента і всім дочірнім компонентам.

x-init

Хук, який дозволяє виконати якусь функцію, до того, як бібліотека продовжить маніпуляції з DOM-деревом В моєму випадку я викликаю функцію зі свого script тегу, передаючи аргумент який у мене зберігається на даний момент у size. Тобто, як ми бачимо данні з x-data доступні у компоненті просто по ключу без додаткового синтаксису. Також функції мають верхньорівневий await, що дозволяє дочекатись результату і просто за допомогою = записати так само в url і оновити тим самим x-data.

x-init="url = await fetchRandomPicUrl(size)"

x-if

Тут все доволі просто. По суті це вбудований синтаксичний цукор до css властивості display: none;, яка має ті самі недоліки, як неможливість застосувати анімацію переходу (transition).

Важливо

Оскільки x-if вирізає елемент з DOM-дерева, його необхідно огорнути у тег template і вже в ньому використовувати сам атрибут.

x-model

Це двостороння привʼязка даних, як в vue, svelte чи angular, але якщо ви з react чи solid, то по суті x-model замінює value та onChange. Тобто ваше значення інпута і данні в стані будуть завжди синхронізовані і впливати один на одного.

Тобто коли ми пишемо щось в input данні в x-state змінюються і якщо ми зімнемо іншим чином данні в x-state вони оновляться одразу в нашому input.

<input required min="100" max="1000" type="number" x-model="size" />

x-on або @

x-on або скорочення @ дозволяє створити listener для подій. У нашому прикладі я використовую скорочення @ яке розробники бібліотеки зробили через часту вживаність цього атрибуту щоб слухати клік кнопки і виконувати новий запит на сервер ідентичний тому, що я описав в x-init

Важливо

Умовний listener x-on:click === @click

x-bind aбо :

Так само, як і попередній атрибут x-bind має скорочення – :. Цей атрибут використовується, для додавання до HTML елементів інших атрибутів, таких як class, style, src або disabled. У моєму прикладі саме останній, який за допомогою простої умови блокує кнопку, якщо значення розміру зображення завелике, чи замале.

:disabled="size < 100 || size > 1000 ? true : false"

Також далі по коду я використовую вже повну версію x-bind:src для того, щоб записувати одразу посилання на зображення, яке приходить з серверу.

<img x-bind:src="url" alt="random picture" />

Короткий висновок і пара думок

Як видно з коду всього за допомогою декількох символів і коротких слів, ми можемо оживити сторінку і додати дуже багато типового функціоналу. Навіть в такому маленькому прикладі ми бачимо і роботу з формами і з DOM-деревом, до того ж покривається одразу декілька різних задач, як оновлення, або умовне відображення. Документація має ще більше цікавинок, таких як store, система плагінів і інші цікавинки.

Моя думка, що AlpineJS підійде для лендінгів, або інших сайтів, де основний контент статичний, але треба мати змогу додати трохи точкової динаміки. Наприклад, якщо ви робите міні-магазин, то зверставши унікальні сторінки для 5 продуктів і написавши корзину на AlpineJS це, буде одне з найкращих рішень в плані швидкості, оптимізації та задоволення від роботи.