Was mir an Schreibstilen mit Svelte gefällt

Avatar of Ollie Williams
Ollie Williams am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In letzter Zeit gab es viel verdienten Hype um Svelte, wobei das Projekt über 24.000 GitHub-Sterne angesammelt hat. Wohl das einfachste JavaScript-Framework, das es gibt, wurde Svelte von Rich Harris geschrieben, dem Entwickler hinter Rollup. Es gibt vieles, was an Svelte gefällt (Performance, integriertes Zustandsmanagement, das Schreiben von korrektem Markup anstelle von JSX), aber für mich war der Hauptgrund seine Herangehensweise an CSS.

Single-File-Komponenten

​​

React hat keine Meinung dazu, wie Stile definiert werden
—React-Dokumentation

​​​​

Ein UI-Framework, das keine eingebaute Möglichkeit zum Hinzufügen von Stilen zu seinen Komponenten hat, ist unvollständig.
—Rich Harris, Schöpfer von Svelte

In Svelte können Sie CSS in einer Stylesheet schreiben, wie Sie es normalerweise in einem typischen Projekt tun würden. Sie können auch CSS-in-JS-Lösungen wie styled-components und Emotion verwenden, wenn Sie möchten. Es ist immer üblicher geworden, Code nach Komponenten zu unterteilen, anstatt nach Dateityp. React erlaubt beispielsweise die Zusammenfassung des Markups und JavaScripts einer Komponente. In Svelte wird dies einen logischen Schritt weiter gedacht: Das JavaScript, das Markup und das Styling für eine Komponente können alle zusammen in einer einzigen `.svelte`-Datei existieren. Wenn Sie jemals Single-File-Komponenten in Vue verwendet haben, wird Svelte Ihnen vertraut vorkommen.

// button.svelte
<style>
  button {
    border-radius: 0;
    background-color: aqua;
  }
</style>

<button>
  <slot/>
</button>

Stile sind standardmäßig gekapselt

Standardmäßig sind die in einer Svelte-Datei definierten Stile *gekapselt*. Ähnlich wie bei CSS-in-JS-Bibliotheken oder CSS Modules generiert Svelte beim Kompilieren eindeutige Klassennamen, um sicherzustellen, dass die Stile eines Elements niemals mit Stilen eines anderen Elements in Konflikt geraten.

Das bedeutet, Sie können einfache Elementselektoren wie div und button in einer Svelte-Komponentendatei verwenden, ohne sich mit Klassennamen auseinandersetzen zu müssen. Wenn wir auf die Button-Stile in unserem früheren Beispiel zurückkommen, wissen wir, dass ein Regelwerk für <button> nur auf unsere <Button>-Komponente angewendet wird – nicht auf andere HTML-Button-Elemente auf der Seite. Wenn Sie mehrere Buttons innerhalb einer Komponente haben und diese unterschiedlich gestalten möchten, benötigen Sie immer noch Klassen. Klassen werden ebenfalls von Svelte gekapselt.

Die von Svelte generierten Klassen sehen wie Kauderwelsch aus, da sie auf einem Hash der Komponentenstile basieren (z. B. svelte-433xyz). Dies ist viel einfacher als eine Namenskonvention wie BEM. Zugegebenermaßen ist die Erfahrung beim Betrachten von Stilen in DevTools etwas schlechter, da die Klassennamen keine Bedeutung haben.

Das Markup einer Svelte-Komponente in DevTools.

Es ist keine Entweder-Oder-Situation. Sie können Sveltes gekapselte Stile zusammen mit einer regulären Stylesheet verwenden. Ich persönlich schreibe komponentenspezifische Stile innerhalb von .svelte-Dateien, nutze aber Utility-Klassen, die in einer Stylesheet definiert sind. Für globale Stile, die in einer gesamten App verfügbar sein sollen – CSS-Custom-Properties, wiederverwendbare CSS-Animationen, Utility-Klassen, beliebige „Reset“-Stile oder ein CSS-Framework wie Bootstrap – empfehle ich, sie in einer Stylesheet abzulegen, die im Head Ihres HTML-Dokuments verlinkt ist.

Es ermöglicht uns, globale Stile zu erstellen

Wie wir gerade gesehen haben, können Sie eine reguläre Stylesheet verwenden, um globale Stile zu definieren. Sollten Sie globale Stile innerhalb einer Svelte-Komponente definieren müssen, können Sie das auch tun, indem Sie :global verwenden. Dies ist im Wesentlichen eine Möglichkeit, die Kapselung zu umgehen, wann und wo Sie es benötigen.

Zum Beispiel möchte eine Modal-Komponente möglicherweise eine Klasse umschalten, um das Body-Element zu stylen

<style>
:global(.noscroll) {
  overflow: hidden;
}
</style>

Nicht verwendete Stile werden markiert

Ein weiterer Vorteil von Svelte ist, dass es Sie während der Kompilierung über nicht verwendete Stile informiert. Mit anderen Worten, es sucht nach Stellen, an denen Stile definiert, aber nie im Markup verwendet werden.

Bedingte Klassen sind prägnant und mühelos

Wenn der Name der JavaScript-Variablen und der Klassenname gleich sind, ist die Syntax unglaublich prägnant. In diesem Beispiel erstelle ich Modifier-Props für einen Full-Width-Button und einen Ghost-Button.

<script>
  export let big = false;
  export let ghost = false;
</script>

<style>
  .big {
    font-size: 20px;
    display: block;
    width: 100%;
  }
  
  .ghost {
    background-color: transparent;
    border: solid currentColor 2px;
  }
</style>    
    
<button class:big class:ghost>
  <slot/>
</button>

Eine Klasse ghost wird auf das Element angewendet, wenn eine ghost-Prop verwendet wird, und eine Klasse big wird angewendet, wenn eine big-Prop verwendet wird.

<script>
  import Button from './Button.svelte';
</script>

<Button big ghost>Click Me</Button>

Svelte verlangt nicht, dass Klassen- und Prop-Namen identisch sind.

<script>
  export let primary = false;
  export let secondary = false;
</script>

<button
  class:c-btn--primary={primary}
  class:c-btn--secondary={secondary}
  class="c-btn">
  <slot></slot>
</button>

Die obige Button-Komponente hat immer eine Klasse c-btn, enthält aber nur dann Modifikator-Klassen, wenn die entsprechende Prop übergeben wird, wie hier

<Button primary>Click Me</Button>

Dies generiert folgendes Markup

<button class="c-btn c-btn--primary">Click Me</button>

Eine beliebige Anzahl von beliebigen Klassen kann mit einer einzigen Prop an eine Komponente übergeben werden

<script>
let class_name = '';
export { class_name as class };
</script>

<button class="c-btn {class_name}">
  <slot />
</button>

Dann können Klassen ähnlich wie bei HTML-Markup verwendet werden

<Button class="mt40">Click Me</Button>

Von BEM zu Svelte

Sehen wir uns an, wie viel einfacher Svelte das Schreiben von Stilen im Vergleich zu einer Standard-CSS-Namenskonvention macht. Hier ist eine einfache Komponente, die mit BEM codiert wurde.

.c-card {
  border-radius: 3px;
  border: solid 2px;
}

.c-card__title {
  text-transform: uppercase;
}

.c-card__text {
  color: gray;
}

.c-card--featured {
  border-color: gold;
}

Mit BEM werden Klassen lang und hässlich. In Svelte sind die Dinge viel einfacher.

<style>
div {
  border-radius: 3px;
  border: solid 2px;
}

h2 {
  text-transform: uppercase;
}

p {
  color: gray;
}

.featured {
  border-color: gold;
}
</style>

<div class:featured>
  <h2>{title}</h2>
  <p>
    <slot />
  </p>
</div>

Es spielt gut mit Präprozessoren zusammen

CSS-Präprozessoren fühlen sich bei der Arbeit mit Svelte viel weniger notwendig an, aber sie können perfekt nebeneinander funktionieren, indem sie ein Paket namens Svelte Preprocess verwenden. Unterstützung ist für Less, Stylus und PostCSS verfügbar, aber hier sehen wir uns Sass an. Das Erste, was wir tun müssen, ist, einige Abhängigkeiten zu installieren

npm install -D svelte-preprocess node-sass

Dann müssen wir autoPreprocess am Anfang der Datei in rollup.config.js importieren.

import autoPreprocess from 'svelte-preprocess';

Als Nächstes suchen wir das Array plugins und fügen preprocess: autoPreprocess() zu Svelte hinzu

export default {
  plugins: [
    svelte({
      preprocess: autoPreprocess(),
      ...other stuff

Dann müssen wir nur noch angeben, dass wir Sass verwenden, wenn wir in einer Komponentendatei arbeiten, indem wir type="text/scss" oder lang="scss" zum Style-Tag hinzufügen.

<style type="text/scss">
  $pink: rgb(200, 0, 220);
  p {
    color: black;
    span {
      color: $pink;
    }
  }
</style>

Dynamische Werte ohne Laufzeit

Wir haben gesehen, dass Svelte die meisten Vorteile von CSS-in-JS Out-of-the-Box mitbringt – aber ohne externe Abhängigkeiten! Allerdings gibt es eine Sache, die Drittanbieterbibliotheken können und Svelte einfach nicht: JavaScript-Variablen in CSS verwenden.

Der folgende Code ist ungültig und funktioniert nicht:

<script>
  export let cols = 4;
</script>

<style>
  ul {
    display: grid;
    width: 100%;
    grid-column-gap: 16px;
    grid-row-gap: 16px;
    grid-template-columns: repeat({cols}, 1fr);
  }
</style>

<ul>
  <slot />
</ul>

Wir können jedoch ähnliche Funktionalität durch die Verwendung von CSS-Variablen erreichen.

<script>
  export let cols = 4;
</script>

<style>
  ul {
    display: grid;
    width: 100%;
    grid-column-gap: 16px;
    grid-row-gap: 16px;
    grid-template-columns: repeat(var(--columns), 1fr);
  }
</style>

<ul style="--columns:{cols}">
  <slot />
</ul>

Ich habe im Laufe der Jahre CSS auf verschiedenste Arten geschrieben: Sass, Shadow DOM, CSS-in-JS, BEM, Atomic CSS und PostCSS. Svelte bietet die intuitivste, zugänglichste und benutzerfreundlichste Styling-API. Wenn Sie mehr über dieses Thema lesen möchten, dann schauen Sie sich das treffend betitelte The Zen of Just Writing CSS von Rich Harris an.