Astro - Einführung

Eine kurze Einführung in Astro einem JavaScript Framework für statische Webseiten.

Astro ist ein JavaScript Framework für statische Webseiten. Die Grundlagen von Astro, wie man damit startet, wie man Komponenten und ein Layout erstellt, das soll diese Einführung zeigen. Dieser Artikel zeigt nur den Einstieg, um sich in bestehenden Projekten zurecht zu finden und diese auch erweitern zu können. Umfangreiches Verständnis erfordert, dass man sich mit der sehr guten Astro Dokumentation auseinandersetzt.

Astro gehört zu den statischen Website Generatoren. Als Entwickler von Web Applikationen mit modernen JavaScript Frameworks wie Angular, React oder Vue, fällt einem der Einstieg besonders leicht. Obwohl man nun auf der Serverseite die Applikation erstellt, ist der Entwicklungsablauf sehr ähnlich.

Dies ist auch ein wichtiger Punkt, warum ich Astro nun für die Erstellung von Webseiten einsetze. Im Artikel Astro - Meine Meinung erfährt man mehr dazu und wie Astro sich in die vielen Frameworks einreiht.

Auch wenn man wohl meist mit einem fertigen Theme startet (Astro - Starten mit einem Theme) und sich auf die Inhalte konzentriert, den man veröffentlichen will, sollte man doch die grundlegenden Konzepte verstehen, damit man Anpassungen wie dem Layout durchführen kann.

Leeres Astro Projekt

Etwas ungewöhnlich startet Astro laut deren Install Doku ein neues Projekt mit npm create astro@latest. Doch create ist ein gültiger Alias für init und npm init kann dasselbe wie das npm execute Tool npx: Wenn man einen Paketnamen als Parameter angibt, dann wird dieses Package falls noch nicht vorhanden, heruntergeladen und ausgeführt. (Details in npm help init)

npm create astro@latest 

Falls das aktuelle Astro Package noch nicht lokal verfügbar ist, wird es runtergeladen und dann wird man nach einem leeren Ordner gefragt (Man kann einen Pfad eingeben, wenn man will. Der Ordner wird auch erzeugt, falls er noch nicht existiert.). Ich starte in einem leeren Ordner und tippe Folgendes ein: .. Dann wählt man das Template aus, bestätigt dass man ein Git Repository erzeugen will und es folgt schließlich noch eine Abfrage ob man TypeScript im Strict mode ausführen will (Kann man später auch im ts.config file ändern). Schließlich werden die npm Packages runtergeladen.

Bei der ersten Frage muss man aktuell zweimal Enter drücken, ich vermute, dass ist ein Bug in der CLI.

❯ npm create astro@latest

? Where would you like to create your new project? » .
√ How would you like to setup your new project? » an empty project
√ Would you like to install npm dependencies? (recommended) ... yes
√ Would you like to initialize a new git repository? (optional) ... yes
√ How would you like to setup TypeScript? » Strict

Anmerkung: Das Tool gibt mehr aus, wie diese Zeilen oben, dies soll nur ungefähr die Antworten zeigen, die ich hier ausgewählt habe. Wichtig dabei ist im Moment dass ich an empty project als Template ausgewählt habe.

Am besten man öffnet jetzt VSCode (Visual Studio Code) und arbeitet dort im Terminal weiter:

code .

In VS Code - Terminal ein- oder ausblenden:
Srg + ö

Die entstandene Filestruktur ist noch sehr übersichtlich. (node_modules Ordner, der die Packages enthält ist ausgeblendet in VSCode.)

files after create

Deployment

Um uns anzusehen, welche Dateien bei einem build Vorgang erzeugt werden, starten wir diesen. Ein Blick in das package.json File verrät welche npm Scripts definiert sind. VSCode listete diese auch in der Explorer View auf, sodass man sie auch via Mausklick starten könnte. Wie man will.

npm run build

Das Ergebnis findet man im Ordner dist. Man sieht, dass die Komponente index.astro im pages Ordner ein index.html File generiert und Dateien im Ordner public werden einfach kopiert.

dist folder

Das sind die Dateien, die man jetzt auf einem WebServer hochladen würde, um sie zu hosten.

Man kann auch nur html Dateien in den pages Ordner legen, oder index.html in Subfolder von pages, auch diese werden beim Build dann entsprechend verarbeitet.

Development

Während der Entwicklung einer Seite verwendet man npm start, dass einen lokalen WebServer betreibt und bei Änderungen in den Dateien, wird der Build Prozess intern getriggert, sodass man schnell das Ergebnis in einem Browser ansehen kann (http://localhost:3000/).

Das kommt einem doch sehr bekannt vor von der Entwicklung von Web Applikationen mit z.B. dem Framework Angular.

In diesem Development Modus wird auch ein JavaScript File zum Browser geschickt, dies ist für die automatische Aktualisieren bei einer Änderung nötig.

Änderungen im public Ordner aktualisieren manchmal nicht automatisch den Browser. Hier muss man das Browserfenster aktualisieren.

Einschub: Mit einem Theme starten

Wie schon erwähnt, ist es eine gute Idee mit einem Theme als Template zu starten. Doch auch zum Lernen von Astro sind Themes sehr gut geeignet. Astro gibt nur wenig Struktur vor, man ist sehr frei, wie man damit arbeitet. Doch das erschwert den Einstieg, daher kann man sich von einem Theme inspirieren lassen.

Man kann sich natürlich auch die anderen Templates mal ansehen, die man beim Generieren der Astro App auswählen kann. Aktuell gibt es folgende zur Auswahl, diese sind jedoch alle sehr einfach:

  • a few best practices (recommended)
  • a personal website starter kit
  • an empty project

Meiner Meinung nach lernt man mehr, wenn man sich ein Theme als Template aussucht. Dazu kann man sich auf der Seite: Astro Themes umsehen.

Wie man mit einem Theme seine Astro Seite aufbaut, erkläre ich im folgendem Artikel: Astro - Starten mit einem Theme.

Markdown

Markdown Files können unter /src/pages abgelegt werden und werden zu HTML konvertiert. Man wird die typischen Dateien, die man in Markdown schreibt, wie Blog Artikel zum Beispiel, allerdings etwas anders strukturieren und nicht im pages Ordner liegen lassen. Dazu weiter unten mehr.

Astro Komponente

Eine Astro Komponente ist eine Datei mit .astro Endung, ein Astro File. Sie besteht aus einem HTML Schnipsel und optional kann open ein mit --- abgegrenzter Bereich sein, der Code enthalten kann.

Eine Komponente ist ein abgeschlossenes System. Man kann lokale Variablen definieren und auch das Styling, das man in dieser Komponente hat, beeinflussen keine anderen Seiten.

Es können im Code-Teil Komponenten inkludiert werden, auch Variablen kann man definieren und diese und die inkludierten Komponenten, im HTML Teil verwenden.

Komponenten die wiederverwendet werden können, legt man im Allgemeinen im Ordner src/components ab, bzw. legt man dort für jede Komponente einen eigenen Unterordner an. Doch diese Komponenten sind nichts anderes wie die Astro Komponenten, die man unter src/pages ablegt. Eine Page ist eine Komponente.

Mittels speziell definierten Variablen, können Properties an Komponenten übergeben werden. Das Wichtigste dabei ist Astro.prop, siehe auch API Reference.

Beispiel: Wrapper Komponente in src/components/Wrapper.astro:

---
const {title} = Astro.props;
---
<h1>{title}</h1>

Verwendung in src/pages/seite.astro:

---
import MyWrapper from '../components/Wrapper.astro';
---
<MyWrapper title="Mein Titel">
  <h2>heading2</h2>
  <p>paragraph</p>
</MyWrapper>

Auch auf den Inhalt zwischen MyWrapper kann man in der Komponente zugreifen und zwar mittels folgendem Tag: <slot />.

Man kann natürlich weit komplexere JavaScript bzw. TypeScript Strukturen verwenden. Auch Daten von einem WebService zu holen ist möglich, aber auch vom Filesystem, um damit Dateien von einem Ordner im Projekt auszulesen.

Anmerkung: Object Destructuring

Dieses ES6 Feature object_destructuring ist häufig unklar, daher hier ein kurzer Einschub, was das const {title} = Astro.props; im obigen Beispiel bedeutet:

const obj = { a: 1, b: 2 };
const { a, b } = obj;
// is equivalent to:
// const a = obj.a;
// const b = obj.b;

Wir könnten in unserem Fall also const title = Astro.props.title; schreiben. Bei nur einem Wert ist es also nicht viel kürzer, interessant wird es wenn man viele Werte aus einem Objekt übernehmen will.

Auch Defaultwerte lassen sich so einfach setzen: const {title= 'Mein Default Titel'} = Astro.props;.

Framework Components

Man kann in Astro Komponenten auch mit anderen Frameworks definieren, z.B. mit React. Dazu muss man allerdings eine sogenannte Integration mit npm installieren und im astro.config.mjs File deklarieren. Dies kann praktisch sein, wenn man schon fertige Komponenten hat, z.B. ein Carousel oder dergleichen, das eben in React geschrieben ist.

Debug

Es gibt auch von Astro schon fertige Komponenten.

Sehr hilfreich ist die Astro Debug Komponente, z.B. zum Ausgeben von Variablen:

---
import { Debug } from 'astro/components';
const { frontmatter } = Astro.props;
---
<Debug {frontmatter} />

Im Browser sieht man dadurch auf der entsprechenden Seite, einen hervorgehobenen Bereich, dort ist die Ausgabe sichtbar. Man muss bedenken, dass diese Variablen ja nicht in den F12 Tools angezeigt werden können, denn diese sind ja am Server vorhanden und nicht am Client.

Debug Einblendung

Während der Entwicklung eignet sich auch console.log() sehr gut, den Output sieht man im Terminal, indem man npm start ausgeführt hat.

Layout

Eine Layout Komponente legt man in /src/layouts ab und sie hat einen <slot /> Tag, ist also ein Wrapper für eine andere Komponente oder Page, ansonsten ist es eine Komponente wie alle anderen auch. Sie hat eben den Inhalt, dass sie z.B. die Grundstruktur für alle Seiten festlegt: Header, Footer, inkludiert das Styling, …

Beispiel: src/layouts/BaseLayout.astro

---
import '~/assets/styles/base.css';
import MetaTags from '~/components/core/MetaTags.astro';
const { meta = {} } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" class="motion-safe:scroll-smooth 2xl:text-[20px]">
  <head>
    <MetaTags {...meta} />
  </head>
  <body class="my-class">
    <slot />
  </body>
</html>

Natürlich kann ein Layout wieder ein Layout inkludieren, so kann man eine sehr saubere Layout-Struktur aufbauen, für verschiedene Arten von Seiten: Seite mit Listen, Detailseiten, …

CSS

Man kann CSS Dateien importieren, wie man im Beispiel oben sieht.

Es kann auch SASS verwendet werden, dazu muss nur ein scss (bzw. sass) File anstatt eines css Files importiert werden, es wird automatisch in CSS gewandelt.

Es ist auch möglich in einer Komponente ein Style Tag einzufügen und mit dem Property is:global kann man den Scope, dass das Styling nur innerhalb des Files (=der Komponente) gültig ist, aufheben.

<style is:global>
    h1{
        border: 1px solid red;
    }
</style>

Markdown mit Layout

Auch ein Markdownfile kann mit einem Layout versehen werden. Das generierte HTML wird in den <slot /> Tag in der Layout Komponente eingefügt. Doch es ist etwas anders, als bei Astro Komponenten. Es gibt ein definiertes Property, das man in den frontmatter Bereicht dazufügen muss und in der Layout Komponente kann man auf die anderen Properties zugreifen über das frontmatter property.

Beispiel eines Markdown Files, dass ein Layout importiert:

---
layout: ../../layouts/md-page-layout.astro
title: test
---

# Überschrift

## Liste
- element1
- element2

md-page-layout.astro:

---
const {frontmatter} = Astro.props;
---
<html>
  <h1>{frontmatter.title}</h1>
  <slot />
</html>

Routing

Dateibasiertes Routing. Wie wir schon beim Builden des Projektes im dist Ordner gesehen haben, werden die Seiten einfach anhand des Dateipfades generiert.

src/pages/ueber/index.astro  -> meineseite.de/ueber
src/pages/ueber/mich.astro   -> meineseite.de/ueber/mich

public/images/bild1.png       -> meineseite.de/images/bild1.png

Anmerkung: Dateien die in src/assets/ liegen werden nicht automatisch beim Builden in den dist Ordner gelegt, sie müssen in Astro Komponenten importiert werden und landen dann erst im Ordner dist.

Markdown Pages in Astro Komponente (Blog Content)

Eine Astro Datei kann auch mehrere Dateien erzeugen. Man erstellt dabei eine Datei, dessen Dateiname einen Parameter in eckigen Klammern enthält. Und diese Datei muss dann eine Funktion getStaticPaths() enthalten, die dann die einzelnen Dateinamen erstellt. Hier ein etwas komplexeres Beispiel, aber so in dieser Art kommt das häufiger vor:

/src/pages/[blogcontent].astro:

---
export async function getStaticPaths() {
  const posts = await Astro.glob('../blogdata/*.md');

  return posts.map(post => ({
    params: {
		blogcontent: post.frontmatter.blogfilename
    },
    props: {
      post
    }
  }));
}

const { Content, frontmatter } = Astro.props.post;
---

<html>
	<head>
		<title>{frontmatter.title}</title>
	</head>
	<body>
		<h1>{frontmatter.title}</h1>
		<p>
	  		Erstellt am {new Date(frontmatter.date).toLocaleDateString()}
		</p>
		<Content />
	</body>
</html>

/src/blogdata/artikel2.md:

---
blogfilename: article2
title: "Artikel 2"
date: "2022-11-13 22:00:00"
---

Ein interessanter Artikel.

Unsere Funktion getStaticPaths() liest alle Dateien im Ordner blogdata aus und erstellt ein Array mit Objekten, die im Objekt params den Parameter definieren, der den Platzhalter im Filenamen ersetzt, in unserem Beispiel ist das blogcontent. Zusätzlich werden noch einige Daten dieses “gedachten” POST calls retourniert, auf die wir dann gleich zugreifen können, bei der Darstellung dieses Files.

Die Ausgabe sieht dann folgendermaßen aus für http://localhost:3000/article2:

Artikel 2
Erstellt am 13.11.2022

Ein interessanter Artikel.

Komponenteninseln

Ein Feature von Astro, auf das sie sehr stolz sind, sind die sogenannten Astro Inseln oder auch Komponenteninseln genannt.

Man erkennt sie anhand der Client Direktiven, z.B.: <BuyButton client:load />

Wenn man interaktive Elemente auf seiner Webseite hat, dann benötigt man natürlich JavaScript. Da JavaScript zu verarbeiten am Client zeitaufwendig ist, wird dadurch die Anzeige der ganzen Seite verzögert. Mit den Astro Islands kann man einzelne Elemente der Seiten als Inseln definieren und wenn nur diese JavaScript benötigen, können die anderen Teile der Seite statisch gerendert und daher sehr schnell angezeigt werden. Eine gute Einleitung dazu sieht man in der Doku zu den Astro Inseln.

Fazit

Dieser Artikel ist nur ein kurzer Abriss über Astro, ein erster Einstieg in das Thema. Ich denke aber, dass man sich damit in bestehenden Projekten gut zurechtfindet und es motiviert hoffentlich weiter zu lernen. Das Framework ist sehr schlau aufgebaut, sodass man sehr flexibel damit statische Websites erstellen kann. Besonders als Frontend JavaScript Entwickler, der mit modernen JavaScript Frameworks arbeitet, hat man keine Schwierigkeiten, da der Entwicklungsprozess sehr ähnlich ist, auch wenn man sich hier auf der Serverseite bewegt.

HLC-astro-intro-twitter


Artikel in diesem Blog


Weitere Astro Artikel.