import ReadMore from '~/components/ReadMore.astro'

Du kannst deinen Astro-Komponenten Interaktivität hinzufügen, ohne ein UI-Framework wie React, Svelte, Vue usw. zu verwenden, indem du standardmäßige HTML-<script>-Tags verwendest. So kannst du JavaScript in den Browser schicken und deine Astro-Komponenten mit Funktionen versehen.

Clientseitige Skripte

Skripte können verwendet werden, um Event-Listener hinzuzufügen, Analysedaten zu senden, Animationen abzuspielen und alles andere, was JavaScript im Web ermöglicht.

astro
<!-- src/components/ConfettiButton.astro --><button data-confetti-button>Zelebrieren!</button><script>  // Importiere npm-Module.  import confetti from 'canvas-confetti';  // Finde unser Komponenten-DOM auf der Seite.  const buttons = document.querySelectorAll('[data-confetti-button]');  // Füge Event-Listener hinzu, um Konfetti zu werfen, wenn eine Schaltfläche angeklickt wird.  buttons.forEach((button) => {    button.addEventListener('click', () => confetti());  });</script>

Standardmäßig verarbeitet und bündelt Astro <script>-Tags und unterstützt damit den Import von npm-Modulen, das Schreiben von TypeScript und mehr.

Verwendung von <script> in Astro

In .astro-Dateien kannst du clientseitiges JavaScript hinzufügen, indem du ein (oder mehrere) <script>-Tags hinzufügst.

In diesem Beispiel wird durch das Hinzufügen der Komponente <Hello /> zu einer Seite eine Nachricht auf der Browserkonsole aufgezeichnet.

astro
<h1>Willkommen, Welt!</h1><script>  console.log('Willkommen, Browser-Konsole!');</script>

Skriptverarbeitung

<script>-Tags werden standardmäßig von Astro verarbeitet.

  • Alle Importe werden gebündelt, sodass du lokale Dateien oder Node-Module importieren kannst.
  • Das verarbeitete Skript wird dort, wo es deklariert ist, mit type="module" eingefügt.
  • TypeScript wird vollständig unterstützt, einschließlich des Imports von TypeScript-Dateien.
  • Wenn deine Komponente mehrmals auf einer Seite verwendet wird, wird das Skript nur einmal eingebunden.
astro
<script>  // Verarbeitet! Gebündelt! TypeScript-unterstützt!  // Der Import von lokalen Skripten und Node-Modulen funktioniert.</script>

Das Attribut type="module" bewirkt, dass der Browser das Skript als JavaScript-Modul behandelt. Das hat mehrere Leistungsvorteile:

  • Das Rendering wird nicht blockiert. Der Browser fährt mit der Verarbeitung des restlichen HTML fort, während das Modulskript und seine Abhängigkeiten geladen werden.
  • Der Browser wartet auf die Verarbeitung von HTML, bevor er die Modulskripte ausführt. Du musst nicht auf das Ereignis load warten.
  • Die Attribute async und defer sind unnötig. Modulskripte werden immer aufgeschoben.

:::note Das Attribut async ist für normale Skripte nützlich, weil es verhindert, dass sie das Rendern blockieren. Modulskripte haben dieses Verhalten jedoch bereits. Wenn du async zu einem Modulskript hinzufügst, wird es ausgeführt, bevor die Seite vollständig geladen ist. Das ist wahrscheinlich nicht das, was du willst. :::

Skript-Verarbeitung deaktivieren

Um zu verhindern, dass Astro ein Skript verarbeitet, füge die Direktive is:inline hinzu.

astro
<script is:inline>  // Wird genau wie geschrieben in das HTML gerendert!  // Lokale Importe werden nicht aufgelöst und funktionieren nicht.  // Wenn es in einer Komponente enthalten ist, wird es jedes Mal wiederholt, wenn die Komponente verwendet wird.</script>

:::note Astro wird deine Skript-Tags in manchen Situationen nicht verarbeiten. Insbesondere das Hinzufügen von type="module" oder eines anderen Attributs als src zu einem <script>-Tag führt dazu, dass Astro den Tag so behandelt, als ob er eine is:inline-Direktive hätte. Das Gleiche gilt, wenn das Skript in einem JSX-Ausdruck geschrieben wird. :::

Siehe unsere Richtlinien-Referenz Seite für weitere Informationen über die Direktiven, die für <script> Tags verfügbar sind.

JavaScript-Dateien auf deiner Seite einbinden

Vielleicht möchtest du deine Skripte als separate .js/.ts-Dateien schreiben oder du musst auf ein externes Skript auf einem anderen Server verweisen. Das kannst du tun, indem du im Attribut src eines <script>-Tags auf diese Dateien verweist.

Lokale Skripte importieren

Wann sollte man das verwenden: wenn dein Skript innerhalb von src/ liegt.

Astro erstellt und optimiert diese Skripte für dich und fügt sie der Seite hinzu, indem es die Skriptverarbeitungsregeln befolgt.

astro
<!-- relativer Pfad zum Skript unter `src/scripts/local.js` --><script src="../scripts/local.js"></script><!-- funktioniert auch für lokale TypeScript-Dateien --><script src="./script-with-types.ts"></script>

Externe Skripte laden

Wann sollte man das verwenden: wenn deine JavaScript-Datei in public/ oder in einem CDN liegt.

Um Skripte außerhalb des Ordners src/ deines Projekts zu laden, füge die Direktive is:inline ein. Dieser Ansatz überspringt die Verarbeitung, Bündelung und Optimierung von JavaScript, die Astro beim Import von Skripten wie oben beschrieben vornimmt.

astro
<!-- absoluter Pfad zu einem Skript unter `public/my-script.js` --><script is:inline src="/my-script.js"></script><!-- vollständige URL zu einem Skript auf einem anderen Server --><script is:inline src="https://my-analytics.com/script.js"></script>

Übliche Skript-Pattern

onclick und andere Ereignisse verarbeiten

Einige UI-Frameworks verwenden eine eigene Syntax für die Ereignisbehandlung wie onClick={...} (React/Preact) oder @click="..." (Vue). Astro hält sich enger an den HTML-Standard und verwendet keine eigene Syntax für Ereignisse.

Stattdessen kannst du addEventListener in einem <script> Tag verwenden, um Benutzerinteraktionen zu verarbeiten.

astro
<button class="alert">Klick mich!</button><script>  // Finde alle Buttons mit der Klasse `alert` auf der Seite.  const buttons = document.querySelectorAll('button.alert');  // Verarbeite Klicks auf allen Buttons.  buttons.forEach((button) => {    button.addEventListener('click', () => {      alert('Button wurde angeklickt!');    });  });</script>

:::note Wenn du mehrere <AlertButton />-Komponenten auf einer Seite hast, führt Astro das Skript nicht mehrfach aus. Skripte werden gebündelt und nur einmal pro Seite eingebunden. Die Verwendung von querySelectorAll stellt sicher, dass dieses Skript den Event-Listener an jede Schaltfläche mit der Klasse alert auf der Seite anhängt. :::

Webkomponenten mit benutzerdefinierten Elementen

Du kannst deine eigenen HTML-Elemente mit benutzerdefiniertem Verhalten mithilfe des Webkomponenten-Standards erstellen. Wenn du ein benutzerdefiniertes Element in einer .astro-Komponente definierst, kannst du interaktive Komponenten erstellen, ohne ein UI-Framework zu benötigen.

In diesem Beispiel definieren wir ein neues <astro-heart> HTML-Element, das aufzeichnet, wie oft du auf den Herz-Button klickst und das <span> mit der Anzahl aktualisiert.

astro
<!-- Schließe die Komponentenelemente in unser benutzerdefiniertes Element "astro-heart" ein. --><astro-heart>  <button aria-label="Heart">💜</button> × <span>0</span></astro-heart><script>  // Definiere das Verhalten für unseren neuen Typ von HTML-Element.  class AstroHeart extends HTMLElement {    connectedCallback() {      let count = 0;      const heartButton = this.querySelector('button');      const countSpan = this.querySelector('span');      // Jedes Mal, wenn die Schaltfläche angeklickt wird, aktualisiere die Anzahl.			heartButton.addEventListener('click', () => {        count++;        countSpan.textContent = count.toString();      });		}  }  // Teile dem Browser mit, dass er unsere AstroHeart-Klasse für <astro-heart>-Elemente verwenden soll.  customElements.define('astro-heart', AstroHeart);</script>

Die Verwendung eines benutzerdefinierten Elements hat hier zwei Vorteile:

  1. Anstatt die gesamte Seite mit document.querySelector() zu durchsuchen, kannst du this.querySelector() verwenden, das nur innerhalb der aktuellen benutzerdefinierten Elementinstanz sucht. Das macht es einfacher, jeweils nur mit den Children einer Komponenteninstanz zu arbeiten.

  2. Obwohl ein <script> nur einmal ausgeführt wird, führt der Browser die Methode constructor() unseres benutzerdefinierten Elements jedes Mal aus, wenn er <astro-heart> auf der Seite findet. Das bedeutet, dass du gefahrlos Code für jeweils eine Komponente schreiben kannst, auch wenn du diese Komponente mehrmals auf einer Seite verwenden willst.

Mehr über benutzerdefinierte Elemente erfährst du in web.dev's Leitfaden für wiederverwendbare Webkomponenten und MDNs Einführung in benutzerdefinierte Elemente.

Übergabe von Frontmatter-Variablen an Skripte

In Astro-Komponenten läuft der Code in Frontmatter zwischen den ----Zeichen auf dem Server und ist im Browser nicht verfügbar. Um Variablen vom Server an den Client zu senden, brauchen wir eine Möglichkeit, unsere Variablen zu speichern und sie dann zu lesen, wenn JavaScript im Browser läuft.

Eine Möglichkeit, dies zu tun, ist die Verwendung von data-*-Attributen, um den Wert von Variablen in deiner HTML-Ausgabe zu speichern. Skripte, einschließlich benutzerdefinierter Elemente, können diese Attribute dann mit der Eigenschaft dataset eines Elements lesen, sobald dein HTML im Browser geladen ist.

In dieser Beispielkomponente wird eine message-Eigenschaft in einem data-message-Attribut gespeichert, sodass das benutzerdefinierte Element this.dataset.message lesen und den Wert der Eigenschaft im Browser abrufen kann.

astro
---const { message = 'Willkommen, Welt!' } = Astro.props;---<!-- Speichere die Nachrichten-Props als Datenattribut. --><astro-greet data-message={message}>  <button>Sag Hallo!</button></astro-greet><script>  class AstroGreet extends HTMLElement {    connectedCallback() {      // Lies die Nachricht aus dem Datenattribut.      const message = this.dataset.message;      const button = this.querySelector('button');      button.addEventListener('click', () => {        alert(message);      });		}  }  customElements.define('astro-greet', AstroGreet);</script>

Jetzt können wir unsere Komponente mehrmals verwenden und werden jedes Mal mit einer anderen Nachricht begrüßt.

astro
---import AstroGreet from '../components/AstroGreet.astro';---<!-- Verwende die Standardnachricht: "Willkommen, Welt!" --><AstroGreet /><!-- Verwende benutzerdefinierte Nachrichten, die als Props übergeben werden. --><AstroGreet message="Schöner Tag, um Bauteile zu bauen!" /><AstroGreet message="Schön, dass du es geschafft hast! 👋" />

:::tip[Wusstest du schon?] Genau das macht Astro hinter den Kulissen, wenn du Props an eine Komponente übergibst, die mit einem UI-Framework wie React geschrieben wurde! Für Komponenten mit einer client:*-Direktive erstellt Astro ein benutzerdefiniertes <astro-island>-Element mit einem props-Attribut, das deine serverseitigen Props in der HTML-Ausgabe speichert. :::

Kombination von Skripten und UI-Frameworks

Elemente, die von einem UI-Framework gerendert werden, sind möglicherweise noch nicht verfügbar, wenn ein <script>-Tag ausgeführt wird. Wenn dein Skript auch mit UI-Framework-Komponenten umgehen muss, empfiehlt es sich, ein eigenes Element zu verwenden.