3D-Grafik von Steve Johnson – https://unsplash.com/@steve_j

Alpine.js in einer Produktions-Umgebung einbinden

Um die Interaktivität einer produktionsreifen PHP-Umgebung zu steigern, habe ich erstmals Alpine.js implementiert. Hier sind meine Erkenntnisse.

Jairus Joer

Jairus Joer

Gründe für die Verwendung von Alpine

Alpine.js beschreibt sich selbst als Ihr neues, schlankes JavaScript-Framework und fokussiert sich genau auf diesen Punkt. Mit den Features von Vue.js und einer ebenso familiären Syntax (dank @vue/reactivity), lässt sich bestehender HTML-Code um ein gewünschtes Maß an Reaktivität erweitern, ohne den Fußabdruck bestehender Skripte großartig zu erhöhen.

Einbindung & Erste Schritte

Die Einbindung von Alpine, unter Berücksichtigung von Datenschutz und aktuellen Content Security Policies ist so einfach, wie es nur sein könnte.

$ pnpm i alpinejs

In den meisten Fällen reicht die empfohlene Implementierung in der Dokumentation. Für ein erhöhtes Maß an Sicherheit liefert Alpine einen dedizierten Build mit @alpinejs/csp.

Nach der Installation des Packages lässt sich Alpine importieren und mit der Zeile window.Alpine.start() initialisieren.

import Alpine from 'alpinejs';

window.Alpine = Alpine; // Optional für Feedback innerhalb der DevTools
window.Alpine.start();

Theoretisch kann Alpine ab diesem Punkt vollumfänglich inline genutzt werden. Im folgenden Beispiel wird ein Dropdown-Element mit Alpine-Attributen gesteuert.

<div class="dropdown" x-data="{open: false}">
  <button class="dropdown-head" x-on:click="open = !open">Hallo</button>
  <div class="dropdown-body" x-cloak x-show="open" x-transition>
    Welt
  </div>
</div>

Das x-cloak-Attribut für .dropdown-body ist nicht zwingend notwendig, unterbindet aber das Flickern der Komponente beim initialen Laden der Seite.

Da Elemente wie Dropdowns häufig auf einer Seite mehrfach im gleichen Kontext eingesetzt werden, kann die Funktionsweise mit globalen Methoden insoweit optimiert werden, dass der tatsächliche Inline-Code etwas schlanker und übersichtlicher ausfällt.

Mehr Flexibilität mit Alpine.data()

Die Methode Alpine.data() ermöglicht die Wiederverwendung von x-data-Kontexten innerhalb der Anwendung. Dadurch kann das bereits präsentierte Dropdown-Element wie folgt umgestaltet werden.

import Alpine from 'alpinejs';

document.addEventListener('alpine:init', () => {
  Alpine.data('dropdown', () => ({
    open: false,
    toggle() {
      this.open = !this.open;
    },
  }));
});

window.Alpine.start();
<div class="dropdown" x-data="dropdown">
  <button class="dropdown-head" x-on:click="toggle">Hallo</button>
  <div class="dropdown-body" x-cloak x-show="open" x-transition>
    Welt
  </div>
</div>

In diesem Szenario wird der Zustand open und die onclick-Funktion auf die globale Methode ausgelagert, sodass Änderungen am Code zentral, innerhalb des Dropdown-Objekts erfolgen können.

Globale und lokale Scopes und x-data als Utility

Wie am Beispiel des Dropdowns verdeutlicht, können globale und lokale Scopes genutzt werden, um Kontext zu schaffen und zu manipulieren. Diese Methodik lässt sich als übergeordnete Utility etablieren, um so multiple Elemente gleichzeitig zu steuern, oder Zustände einheitlich zu definieren und zu ordnen.

In der bereits erwähnten PHP-Umgebung wurden Video-Inhalte eines populären Drittanbieters eingebunden, welche die Zustimmung der Nutzer:innen zum Datenaustausch erfordern. Mit Alpine habe ich eine Methode definiert, welche alle Videos des Anwenders innerhalb des x-data-Scopes videos lädt und einblendet, insofern Nutzer:innen dem Datenaustausch zustimmen.

Alpine.data('videos', () => ({
  show: false,
  embed: 'https://www.youtube-nocookie.com/embed/',
  accept: 'YouTube-Inhalte erlauben',
  decline: 'YouTube-Inhalte ausblenden',

  toggle() {
    this.show = !this.show;
  },

  src(id) {
    return this.show && this.embed + id;
  },

  text() {
    return !this.show ? this.accept : this.decline;
  },
}));
<div class="videos" x-data="videos">
  <!-- ... -->
  <div class="video">
    <div class="video-consent" x-show="!show" x-transition>...</div>
    <iframe
      class="video-frame"
      title="Video-Player"
      x-cloak
      x-bind:src="src('dQw4w9WgXcQ')"
      x-show="show"
      x-transition
    ></iframe>
    <button class="video-button" x-on:click="toggle" x-text="text"></button>
  </div>
  <!-- ... -->
</div>

Prinzipiell lässt sich dieses Konzept modularisieren und für verschiedene Drittanbieter optimieren, sodass eine Methodik für mehrere Fälle angewendet werden kann.

Erkenntnisse

Komplexe und mehrfach eingesetzte Funktionen können in einem Kontext zusammengefasst und durch x-data für untergeordnete Elemente zugänglich gemacht werden. Die Möglichkeit, Skripte innerhalb der Alpine-Attribute zu verfassen und zu testen, lädt zwar zum Experimentieren ein, ist jedoch für eine Produktionsumgebung ungeeignet. Funktionen und Methoden können stattdessen innerhalb des 'alpine:init' EventListener definiert werden, um die Ladezeit und Komplexität des HTML-Dokuments zu reduzieren.

Globale Events und Elemente lassen sich exzellent über einen Kontext im <html>-Element verwalten, da dieser allen untergeordneten Elementen zusteht.

<html lang="de" x-data="utility">

Der globale Kontext blockiert jedoch nicht die Verwendung weiterer Kontexte innerhalb der HTML-Struktur. Dadurch können zwei Kontexte innerhalb eines Elements zur Verfügung stehen.

<html lang="de" x-data="utility">
  <!-- ... -->
  <div class="videos" x-data="videos"></div>
    <!-- ... -->
</html>

Somit kann für jede HTML-Komponente ein eigener Kontext mit Funktionen und Daten etabliert werden, welcher auch Zugriff auf übergeordneten Kontext besitzt und von globalen Einstellungen nicht ausgeschlossen ist.


TL;DR

Alpine ermöglicht den inkrementellen Ausbau um interaktive und reaktive Elemente durch einen reduzierten Ansatz und einer Methodik, welche – dank einer übersichtlichen Dokumentation – schnell aufgegriffen und etabliert werden kann.