Ist Svelte der nächste grosse Frontend-Trend?

21.06.2021Tom Trapp
Mobile Svelte sveltejs JavaScript Frontend Framework Hands-on Tutorial How-to

Schon wieder ein FE Framework?

Nein – Svelte (oder Svelte JS) ist nicht wie Angular, React oder VueJs ein klassisches Framework, Svelte ist ein Open Source JavaScript Compiler welcher deklarativen Code in vanilla JavaScript umwandelt.

Svelte an sich liefert die klassischen Möglichkeiten eines JavaScripts Frameworks wie deklarative Templates inkl. Loops, If-Clauses etc. in einer sehr einfachen Syntax. Andere Frameworks, beispielsweise Angular, liefern eine Menge an Funktionalitäten und Polyfills (die Runtime des Frameworks) mit an der Browser aus. Das eigentliche Auflösen der Framework-eigenen Funktionen findet im Browser statt. Dies bedeutet, dass dem Browser erst das Framework ‘beigebracht’ werden muss. Somit wird das komplette Runtime Framework benötigt, um die Anwendung starten und nutzen zu können.

Svelte dagegen löst sämtliche hauseigenen Funktionen während der Compile-Zeit auf und liefert dem Browser so ein für ihn voll verständliches vanilla JavaScript. In diesem Bundle befinden sich somit nur die effektiv benötigten Funktionen der Anwendung.

In folgender Grafik ist dies auf einen Blick zu sehen.

Die in blau eingefärbten Komponenten sind eigenentwickelte Bausteine der Applikation. Das Framework (rot) bzw. die Funktionen in Grün sind die jeweiligen Bausteine, Helper und Core-Funktionalitäten des Frameworks bzw. von Svelte.

Svelte difference to other frameworks like Angular, React or VueJS

Vergleicht man beide Arten fällt schnell auf, dass bei einem klassischen Framework wie Angular das ganze Framework inklusiver alles nicht benötigten oder verwendeten Funktionen in das Artefakt eingepackt wird. Bei Svelte dagegen sort der Compiler dafür, dass nur benötigte Funktionen eingepackt werden und die Anweisungen direkt verständlich sind und ohne zusätzliche Runtime ausgeführt werden können.

Vor- & Nachteile

Eins der grössten Vorteile von Svelte liegt klar auf der Hand, es ist kein schweres und umfangreiches Framework, dadurch ist das Artefakt sehr leichtgewichtig und von jedem Browser interpretierbar (da vanilla JS). Schaut man sich diesen Vorteil genauer an, fällt schnell auf, das Svelte kein virtual DOM nutzt und dadurch einiges an Performance gewinnt. Bei einem virtual Dom werden deklarative Anweisungen vom Browser on DOM Anweisungen konvertiert und nach einen virtual Dom diffing dann angewendet. Dies führt dazu, dass der Browser viel zu tun hat!

Svelte dagegen konvertiert die Komponenten zur Build Zeit in imperativen Code, welcher “chirurgisch” einzelne Teile des DOMs (Real Dom) updated. So spart man auf der einen Seite enorm an der Bundle Size, da nur die effektiv benötigten Funktionen eingepackt werden und nicht der gesamte ‘Framework Overhead’. Auf der anderen Seite aber spart man enorm an der Ladezeit und generell der Performance im Browser, der Code ist direkt verständlich und die Anweisungen können ohne Diffing, Konvertierung o. ä. angewandt werden. In allen Benchmarks Tests liegt Svelte in allen Themen wie Bundle Sizes, Performance, Startup Time, Build Time, Time To First Screen usw. an erster Spitze.

Ein weiterer Vorteil ist die sehr schlanke Syntax von Svelte beispielsweise im Vergleich zu React oder VueJS.

Einen offensichtlichen Nachteil gibt es nicht, wichtig ist sicher zu erwähnen, dass hinter Svelte keine grosse Firma, sondern eine grosse und aktive Community steht.

Woraus besteht ein Svelte-Projekt?

  • rollup.config.js – Module Bundler ähnlich wie Webpack, Config File dafür
  • package.json – NPM Config mit Dependencies und Build Anweisungen
  • src/main.js – Javascript Einstiegspunkt der Applikation
  • src/App.svelte – Svelte Einstiegspunkt

Im ‘public’ Folder findet man globale CSS Styles sowie die generierten Files bundle.js und bundle.css. Nimmt man noch die index.html dazu liegt unter public das fertig gebaute Svelte Projekt. Hier findet man aber keinerlei Svelte Syntax oder Commands mehr da alles nach vanilla Html, CSS und JavaScript compiled wurde.

Generell biete Svelte auch Support für TypeScript und CSS Pre Processor wie SCSS.

Svelte To-Do List

Wie bereits in den anderen TechUps der Frontend-Serie wollen wir auch hier eine To Do Liste gemeinsam implementieren und Svelte kennenlernen.

Um ein neues Projekt zu startet, bietet Svelte ein Template, welches mit folgendem Command heruntergeladen werden kann. Anschliessend wechseln wir in diesen Ordner, installieren die Dependencies und starten den dev Server.

1
2
3
4
5
npx degit sveltejs/template b-nova-todo-list-svelte
cd b-nova-todo-list-svelte

npm install
npm run dev

So schnell geht es, unser Svelte Projekt ist ready und wir sehen unter http://localhost:5000/ die ‘Hello World’ Beispielseite.

Zunächst wollen ein VS Code Svelte Plugin installieren und das Projekt öffnen.

1
2
code --install-extension svelte.svelte-vscode
code .

Nun wollen wir unsere ersten To-Do Items anlegen und anzeigen, dafür passen wir alle drei Bestandteile einer Svelte Komponente an:

  • <script> – beinhaltet die Logik der Komponente in JavaScript oder TypeScript (kann speziell aktiviert werden, offiziell supported) inkl. Variabel Definitionen usw.
  • Html Block – beinhaltet das Markup der Komponente
  • <style> – beinhaltet das Styling der Komponente, betrifft nur diese Komponente

Die App.svelte sieht nun wie folgt aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>
  let toDos = [
    {
      content: "Get tea",
      completed: true,
    },
    {
      content: "Learn Svelte",
      completed: false,
    },
    {
      content: "Write blog post",
      completed: false,
    },
  ];
</script>

<main>
  <h1>b-nova To-Do List Svelte</h1>
  {#if toDos.length !== 0}
    <div class="container">
      {#each toDos as toDo}
        <div class="todo">
          <p class="content {toDo.completed ? "completed" : "open"}">
            {toDo.content}
          </p>
        </div>
      {/each}
    </div>
  {:else}
    No ToDo Items
  {/if}
</main>

<style>
  .completed {
    text-decoration: line-through;
  }
  .open {
    color: red;
  }
</style>

Die erste eigene Komponente

Im nächsten Schritt wollen wir eine eigene ToDo Komponente implementieren und einen Button, um ein To-Do Item abzuhaken können. Dafür erstellen wir unter /src eine Datei mit dem Namen ToDo.svelte und kopieren das Paragraph-Tag sowie den kompletten Style Block. Zusätzlich müssen wir noch definieren, dass die Variable toDo beim Aufruf der Komponente gesetzt wird, hierfür nutzen wir das Keyword export.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<script>
  export let toDo;
</script>

<div class="todo">
  <p class="content {toDo.completed ? "completed" : "open"}">
    {toDo.content}
  </p>
</div>

<style>
  .completed {
    text-decoration: line-through;
  }
  .open {
    color: red;
  }
</style>

Nun passen wir die App.svelte Komponente entsprechen an, indem wir zuerst den Style Block komplett löschen. Anschliessend wollen wir im Each Loop unsere ToDo Komponente für jedes Item einmal einbinden. Hierfür müssen wir die Komponente zuerst importen und dann entsprechend mit dem toDo Parameter aufrufen bzw. einbinden.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script>
  import ToDo from "./ToDo.svelte";
  ...
</script>

<main>
  <h1>b-nova To-Do List Svelte</h1>
  {#if toDos.length !== 0}
    <div class="container">
      {#each toDos as toDo}
        <ToDo {toDo} />
      {/each}
    </div>
  {:else}
    No ToDo Items
  {/if}
</main>

Als nächsten wollen wir unsere To Do Liste mit etwas mehr Funktionen erweitern, ein To Do Item soll als erledigt oder offen markiert werden können und es soll gelöscht werden können. Hierfür passen wir unsere ToDo.svelte Komponente an und implementieren zwei Buttons mit Click Handler Methoden. In Svelte können wir mit dem on: Syntax auf jedes beliebige Browser-Event wie z. B. Click eine Methode binden.

Unser Script und Html Blog sieht nun wie folgt aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//ToDo.svelte
<script>
  export let toDo;

  function deleteToDo() {
    console.log("Deleted ToDo: " + toDo.content);
  }

  const toggleToDo = () => {
    toDo.completed = !toDo.completed;
  };
</script>


<div class="todo">
  <p class="content {toDo.completed ? 'completed' : 'open'}">
    {toDo.content}
  </p>
  <button on:click={toggleToDo}>{toDo.completed ? "Uncheck" : "Check"}</button>
  <button on:click={deleteToDo}>Delete</button>
</div>

Die Check- und Uncheck-Funktionalität ist so komplett implementiert. Wie im Beispiel zu sehen ist, können Methode einmal als normale Function oder auch im Arrow Syntax deklariert werden.

Event-Dispatching

bei Löschen eines To Do Items müssen wir ein Event nach Aussen geben, dass dieses Item gelöscht werden soll. Grund dafür ist, dass die Definition und “Speicherung” der To Do Liste in der App.svelte Komponente angesiedelt ist, diese Komponente muss über die Löschung informiert werden. Hierfür biete Svelte eine sehr einfache Möglichkeit, wir können mit der Methode createEventDispatcher einen Event Dispatcher generieren und darüber custom Event zu senden.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ToDo.svelte
<script>
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  function deleteToDo() {
    dispatch("deleteToDo", {
      content: toDo.content,
    });
  }
...

Unser deleteToDo Event können wir dann überall, wo die To Do Komponente eingebunden wird, auf eine Methode binden. Auch hierfür nutzen wir, analog dem Click Binding, das Keyword on::

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// App.svelte
...
  const deleteToDo = (event) => {
    toDos = toDos.filter((toDo) => toDo.content !== event.detail.content);
  };
</script>

<main>
  <h1>b-nova To-Do List Svelte</h1>
  {#if toDos.length !== 0}
    <div class="container">
      {#each toDos as toDo}
        <ToDo {toDo} on:deleteToDo={deleteToDo} />
      {/each}
    </div>
  {:else}
    No ToDo Items
  {/if}
</main>

In der deleteToDo Methode können wir über event.detail auf den Body des Events zugreifen und das Element so aus der Liste löschen. Wichtig ist hier, dass wir die gesamte Liste erneut der Variable zuweisen.

Neues To Do Item hinzufügen

Zu guter Letzt wollen wir das neuanlegen eines To Do Items in einer separaten Komponente implementieren. Hierfür erstellen wir eine neue Datei unter /src mit den Namen CreateToDo.svelte und implementieren ein Input Feld inkl. Speichern Button. Auch hier bietet Svelte eine einfache Funktion, um eine Variable auf ein Inputfeld zu binden, konkret nutzen wir hierfür bind. Hier handelt es sich um ein Two Way Binding, daher können wir nach dem Erstellen des To Dos die Variable und somit das Textfeld wieder leeren.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//CreateToDo.svelte
<script>
  import { createEventDispatcher } from "svelte";

  let content = "";

  const dispatch = createEventDispatcher();

  const createToDo = () => {
    dispatch("createToDo", {
      content: content,
    });
    content = "";
  };
</script>

<input class="todo-input" bind:value={content} />
<button class="todo-submit" on:click={createToDo}>Create</button>

Nun müssen wir nur noch, wie in den vorherigen Schritten, die neue Komponente in der App.svelte einbinden und das Event entsprechend behandeln.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//App.svelte

<script>
  import CreateToDo from "./CreateToDo.svelte";
  
  ...
    
  const createToDo = (event) => {
    toDos = [
      ...toDos,
      {
        content: event.detail.content,
        completed: false,
      },
    ];
  };
</script>

<main>
  <h1>b-nova To-Do List Svelte</h1>
  <CreateToDo on:createToDo={createToDo} />
  ...
</main>

Nun ist auch diese Funktion vollständig implementiert und unsere b-nova To Do Liste ist in Svelte in der ersten Version fertig.

Styling

Betreffend Styling bietet Svelte uns generell zwei Optionen, komponentenspezifische oder generelle Styles.

Zuerst wollen wir Styles für das Input Feld und die Buttons Global angeben, dies machen wir im /public/global.css indem wir folgenden Code ans Ende anhängen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.todo-input {
  display: block;
  width: 100%;
  padding: 10px 15px;
  appearance: none;
  border: none;
  background-color: #f3f3f3;
  margin-top: 15px;
  font-size: 20px;
  outline: none;
}

.todo-btn {
display: block;
width: 100%;
max-width: 200px;
appearance: none;
border: none;
outline: none;
background: none;

background-color: #fe4880;
color: #fff;
margin: 15px auto;

padding: 10px 15px;
font-size: 18px;
font-weight: 700;
}

Anschliessend wollen wir einen separaten Style für unsere To Do Component definieren, indem wir am Schluss einen style Block einfügen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
  .completed {
    text-decoration: line-through;
  }
  .open {
    color: red;
  }

  .content {
    flex: 1 1 100%;
  }

  .todo {
    display: flex;
    padding: 10px 15px;
    background-color: #fff;
    border-bottom: 1px solid #ddd;
  }

  .todo:nth-child(even) {
    background-color: #eee;
  }

  .todo:last-of-type {
    border-bottom: 0;
  }

  .todo-btn {
    background-color: #258814 !important;
  }
</style>

Wie wir auf der Page direkt sehen wurden die Styles korrekt applied und das Überschreiben der CSS-Rules in der ToDo.svelte Komponente zählt auch nur für diese Komponente:

b-nova To Do List Svelte

Erstes Artefakt generieren

Ein fertiges Artefakt kann mit folgendem Command generiert werden und wird in den Ordner /public abgelegt.

1
npm run build

Und fertig ist unser ersten Svelte Projekt! 😀

#Fazit & Ausblick

Wir haben nun eine vollständige To Do in Svelte implementiert, es können Aufgaben angezeigt, angelegt, abgehakt und gelöscht werden.

Anschliessend kann man sagen – dass Svelte für kleine Projekte, Single Pages Applications oder Micro Frontends super ist!

Wird der Projektaufbau grösser und komplexer, braucht es verschiedene Pages inkl. Absicherung auf Rollen oder Logins usw., scheint Svelte auf den ersten Blick nicht zu 100 % passend. Hier würden z. B. Angular eher den Wünschen und Anforderungen entsprechen.

Wir bei b-nova haben in kürzester Zeit und ohne grossen Aufwand die erste interne Applikation nach Svelte migriert!

Selbstverständlich ist auch dieses TechUp auf GitHub zu finden.

In einem der nächsten TechUps wollen wir uns die Erweiterung SvelteKit genauer anschauen, die Unterschiede und Vorteile genauer beleuchtet und den Real Life Use Case versuchen zu finden.

Sie benötigen eine schlankes Micro Frontend mit Schnittstellen zu skalierbaren Microservices im Backend? Melden Sie sich bei uns!

Tom Trapp

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.