Eine kurze Einführung in Svelte aus der Perspektive eines Angular-Entwicklers

Svelte ist ein relativ neues UI-Framework, das von Rich Harris entwickelt wurde , der auch der Autor des Rollup-Builders ist. Höchstwahrscheinlich wird Svelte völlig anders aussehen als das, was Sie zuvor behandelt haben, aber vielleicht ist das sogar gut. Die beiden beeindruckendsten Merkmale dieses Frameworks sind Geschwindigkeit und Einfachheit. In diesem Artikel konzentrieren wir uns auf die zweite.



Da meine Hauptentwicklungserfahrung mit Angular zusammenhängt, versuche ich natürlich, Svelte zu lernen, indem ich die mir bereits bekannten Ansätze kopiere. Und genau darum geht es in diesem Artikel: Wie man in Svelte die gleichen Dinge macht wie in Angular.


Hinweis: Trotz der Tatsache, dass ich in einigen Fällen meine Präferenz ausdrücken werde, ist der Artikel kein Vergleich von Frameworks. Dies ist eine schnelle und einfache Einführung in Svelte für Personen, die Angular bereits als Hauptrahmen verwenden.


Warnung Spoiler: Svelte macht Spaß.


Komponenten


In Svelte ist jede Komponente der Datei zugeordnet, in die sie geschrieben wurde. Beispielsweise wird die Button Komponente durch Benennen der Button.svelte Datei erstellt. Natürlich machen wir das normalerweise auch in Angular, aber bei uns ist es nur eine Konvention. (In Svelte stimmt der Name der zu importierenden Komponente möglicherweise auch nicht mit dem Dateinamen überein - Hinweis des Übersetzers)


Svelte-Komponenten sind Einzeldateien und bestehen aus drei Abschnitten: script , style und eine Vorlage, die nicht in ein spezielles Tag eingeschlossen werden muss.


Lassen Sie uns eine sehr einfache Komponente erstellen, die "Hello World" anzeigt.


hallo_welt


Komponenten importieren


Im Allgemeinen ähnelt dies dem Importieren einer JS-Datei, jedoch mit einigen Einschränkungen:


  • Sie müssen die .svelte der .svelte Komponente explizit angeben
  • Komponenten werden in das <script> importiert

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

Aus den obigen Ausschnitten geht hervor, dass die Anzahl der Zeilen zum Erstellen einer Komponente in Svelte unglaublich gering ist. Natürlich gibt es einige Implizitäten und Einschränkungen, aber gleichzeitig ist alles einfach genug, um sich schnell daran zu gewöhnen.


Grundlegende Syntax


Interpolation


Interpolationen in Svelte sind denen in React ähnlicher als in Vue oder Angular:


 <script> let someFunction = () => {...} </script> <span>{ 3 + 5 }</span> <span>{ someFunction() }</span> <span>{ someFunction() ? 0 : 1 }</span> 

Ich bin es gewohnt, doppelte geschweifte Klammern zu verwenden, daher bin ich manchmal versiegelt, aber vielleicht habe ich nur dieses Problem.


Attribute


Das Übergeben von Attributen an Komponenten ist ebenfalls recht einfach. Anführungszeichen sind optional und es können beliebige Javascript-Ausdrücke verwendet werden:


 //Svelte <script> let isFormValid = true; </script> <button disabled={!isFormValid}></button> 

Ereignisse


Die Syntax für Ereignishandler lautet: on:={} .


 <script> const onChange = (e) => console.log(e); </script> <input on:input={onChange} /> 

Im Gegensatz zu Angular müssen wir keine Klammern nach dem Funktionsnamen verwenden, um ihn aufzurufen. Wenn Sie Argumente an den Handler übergeben müssen, verwenden Sie einfach die anonyme Funktion:


 <input on:input={(e) => onChange(e, 'a')} /> 

Meine Ansicht zur Lesbarkeit eines solchen Codes:


  • Wir müssen weniger drucken, weil wir keine Anführungszeichen und Klammern benötigen - das ist sowieso gut.
  • Lesen Sie besser. Ich habe den Angular-Ansatz immer eher gemocht als reagiert, daher ist es für mich und Svelte schwieriger, ihn zu wählen. Aber das ist nur meine Gewohnheit und meine Meinung ist etwas voreingenommen.

Strukturrichtlinien


Im Gegensatz zu strukturierten Direktiven in Vue und Angular bietet Svelte eine spezielle Syntax für Schleifen und Verzweigungen in Vorlagen:


 {#if todos.length === 0}    {:else} {#each todos as todo} <Todo {todo} /> {/each} {/if} 

Gefällt mir sehr. Es werden keine zusätzlichen HTML-Elemente benötigt, und in Bezug auf die Lesbarkeit sieht dies erstaunlich aus. Leider befindet sich das Symbol # im britischen Tastaturlayout meines Macbooks an einer unzugänglichen Stelle, was sich negativ auf meine Erfahrung mit diesen Strukturen auswirkt.


Eingabeeigenschaften


Das Definieren von Eigenschaften, die an eine Komponente übergeben werden können (analog zu @Input in Angular), ist so einfach wie das Exportieren einer Variablen aus einem JS-Modul mit dem Schlüsselwort export . Vielleicht kann es zunächst verwirrend sein - aber lassen Sie uns ein Beispiel schreiben und sehen, wie einfach es wirklich ist:


 <script> export let todo = { name: '', done: false }; </script> <p> { todo.name } { todo.done ? '✓' : '✕' } </p> 

  • Wie Sie sehen können, haben wir die todo Eigenschaft zusammen mit dem Wert initialisiert: todo standardmäßig der Wert der Eigenschaft, falls sie nicht von der übergeordneten Komponente übergeben wird

Erstellen Sie nun einen Container für diese Komponente, der Daten an sie überträgt:


 <script> import Todo from './Todo.svelte'; const todos = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; </script> {#each todos as todo} <Todo todo={todo}></Todo> {/each} 

Ähnlich wie Felder in einem regulären JS-Objekt kann todo={todo} wie folgt gekürzt und neu geschrieben werden:


 <Todo {todo}></Todo> 

Anfangs kam es mir seltsam vor, aber jetzt finde ich es genial.


Ausgabeeigenschaften


Um das Verhalten der @Output Direktive zu implementieren, verwenden wir createEventDispatcher die in Svelte verfügbare Funktion createEventDispatcher , wenn die übergeordnete Komponente Benachrichtigungen vom createEventDispatcher @Output .


  • Importieren Sie die Funktion createEventDispatcher und weisen Sie der createEventDispatcher ihren Rückgabewert zu
  • Die dispatch hat zwei Parameter: den Namen des Ereignisses und die Daten (die in das detail des Ereignisobjekts fallen)
  • Wir markDone dispatch in die markDone Funktion ein, die vom click-Ereignis aufgerufen wird ( on:click ).

 <script> import { createEventDispatcher } from 'svelte'; export let todo; const dispatch = createEventDispatcher(); function markDone() { dispatch('done', todo.name); } </script> <p> { todo.name } { todo.done ? '✓' : '✕' } <button on:click={markDone}></button> </p> 

In der übergeordneten Komponente müssen Sie einen Handler für das Ereignis done erstellen, damit Sie die erforderlichen Objekte im todo markieren können.


  • Erstellen Sie die onDone Funktion
  • Wir weisen diese Funktion dem Ereignishandler zu, der in der on:done={onDone} Komponente wie folgt aufgerufen wird: on:done={onDone}

 <script> import Todo from './Todo.svelte'; let todos = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; function onDone(event) { const name = event.detail; todos = todos.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); } </script> {#each todos as todo} <Todo {todo} on:done={onDone}></Todo> {/each} 

Hinweis: Um Änderungen an einem Objekt zu erkennen, wird das Objekt selbst nicht mutiert. Stattdessen weisen wir der Variablen todos ein neues Array zu, in dem das Objekt der gewünschten Aufgabe bereits in "Abgeschlossen" geändert wird.


Daher wird Svelte als wirklich reaktiv angesehen : Bei der üblichen Zuweisung eines Werts zu einer Variablen ändert sich der entsprechende Teil der Darstellung.


ngModel


Svelte hat eine spezielle bind:<>={} um bestimmte Variablen an die Attribute einer Komponente zu binden und miteinander zu synchronisieren.


Mit anderen Worten, Sie können die bidirektionale Datenbindung organisieren:


 <script> let name = ""; let description = ""; function submit(e) { //    } </script> <form on:submit={submit}> <div> <input placeholder="" bind:value={name} /> </div> <div> <input placeholder="" bind:value={description} /> </div> <button> </button> </form> 

Reaktive Ausdrücke


Wie wir bereits gesehen haben, reagiert Svelte auf das Zuweisen von Werten zu Variablen und zeichnet die Ansicht neu. Sie können auch reaktive Ausdrücke verwenden, um auf Änderungen des Werts einer Variablen zu reagieren und den Wert einer anderen Variablen zu aktualisieren.


Erstellen wir beispielsweise eine Variable, die uns zeigen soll, dass im todos Array alle Aufgaben als erledigt markiert sind:


 let allDone = todos.every(({ done }) => done); 

Die Ansicht wird jedoch nicht neu gezeichnet, wenn das Array aktualisiert wird, da der Wert der Variablen allDone nur einmal zugewiesen wird. Wir werden einen reaktiven Ausdruck verwenden, der uns gleichzeitig an die Existenz von "Labels" in Javascript erinnert:


 $: allDone = todos.every(({ done }) => done); 

Es sieht sehr exotisch aus. Wenn es Ihnen so vorkommt, als gäbe es „zu viel Magie“, erinnere ich Sie daran, dass Tags gültiges Javascript sind .


Eine kleine Demo, die das oben Genannte erklärt:
Demo


Inhaltsinjektion


Zum Einbetten von Inhalten werden auch Slots verwendet, die an der richtigen Stelle innerhalb der Komponente platziert werden.


Um einfach den Inhalt anzuzeigen, der innerhalb des Komponentenelements übertragen wurde, wird ein spezielles slot Element verwendet:


 // Button.svelte <script> export let type; </script> <button class.type={type}> <slot></slot> </button> // App.svelte <script> import Button from './Button.svelte'; </script> <Button>  </Button> 

In diesem Fall ersetzt die "" das Element <slot></slot> .
Benannte Slots müssen benannt werden:


 // Modal.svelte <div class='modal'> <div class="modal-header"> <slot name="header"></slot> </div> <div class="modal-body"> <slot name="body"></slot> </div> </div> // App.svelte <script> import Modal from './Modal.svelte'; </script> <Modal> <div slot="header">  </div> <div slot="body">  </div> </Modal> 

Lebenszyklus-Haken


Svelte bietet 4 Lifecycle-Hooks an, die aus dem svelte Paket importiert werden.


  • onMount - Wird aufgerufen, wenn eine Komponente im DOM bereitgestellt wird
  • beforeUpdate - wird vor dem Aktualisieren der Komponente aufgerufen
  • afterUpdate - wird nach der Komponentenaktualisierung aufgerufen
  • onDestroy - wird aufgerufen, wenn eine Komponente aus dem DOM entfernt wird

Die onMount Funktion verwendet als Parameter eine Rückruffunktion, die aufgerufen wird, wenn die Komponente im DOM platziert wird. Einfach ausgedrückt ähnelt es der Hook- ngOnInit .


Wenn die Rückruffunktion eine andere Funktion zurückgibt, wird sie aufgerufen, wenn die Komponente aus dem DOM entfernt wird.


 <script> import { onMount, beforeUpdate, afterUpdate, onDestroy } from 'svelte'; onMount(() => console.log('', todo)); afterUpdate(() => console.log('', todo)); beforeUpdate(() => console.log('  ', todo)); onDestroy(() => console.log('', todo)); </script> 

Es ist wichtig zu onMount dass beim Aufrufen von onMount alle darin enthaltenen Eigenschaften bereits initialisiert sein müssen. Das heißt, im obigen Fragment sollte todo bereits existieren.


Staatsverwaltung


Die Verwaltung Ihres Staates in Svelte ist unglaublich einfach, und vielleicht sympathisiert dieser Teil des Frameworks mehr als jeder andere mit mir. Sie können die Ausführlichkeit von Code vergessen, wenn Sie Redux verwenden. Zum Beispiel erstellen wir in unserer Anwendung Speicher für die Speicherung und Aufgabenverwaltung.


Aufzeichnbare Tresore


Zuerst müssen Sie das writable Speicherobjekt aus dem svelte/store Paket importieren und ihm den Anfangswert initialState


 import { writable } from 'svelte/store'; const initialState = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; const todos = writable(initialState); 

Normalerweise todos.store.js ich ähnlichen Code in eine separate Datei wie todos.store.js und exportiere die Speichervariable daraus, damit die Komponente, in die ich sie importiere, damit arbeiten kann.


Offensichtlich ist das todos Objekt jetzt ein Repository geworden und kein Array mehr. Um den Speicherwert zu erhalten, verwenden wir in Svelte ein wenig Magie:


  • Durch Hinzufügen des Zeichens $ zum Namen der Speichervariablen erhalten wir direkten Zugriff auf ihren Wert!

Daher ersetzen wir im Code einfach alle Verweise auf die Variable todos durch $todos :


 {#each $todos as todo} <Todo todo={todo} on:done={onDone}></Todo> {/each} 

Statuseinstellung


Der neue Wert des beschreibbaren Speichers kann durch Aufrufen der set Methode angegeben werden, die den Status unbedingt entsprechend dem übergebenen Wert ändert:


 const todos = writable(initialState); function removeAll() { todos.set([]); } 

Statusaktualisierung


Um den Speicher (in unserem Fall todos ) basierend auf seinem aktuellen Status zu update , müssen Sie die update aufrufen und eine Rückruffunktion übergeben, die den neuen Status für den Speicher zurückgibt.


Wir schreiben die zuvor erstellte onDone Funktion neu:


 function onDone(event) { const name = event.detail; todos.update((state) => { return state.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); }); } 

Hier habe ich einen Reduzierer direkt in der Komponente verwendet, was eine schlechte Praxis ist. Wir verschieben es in eine Datei mit unserem Repository und exportieren eine Funktion daraus, die einfach den Status aktualisiert.


 // todos.store.js export function markTodoAsDone(name) { const updateFn = (state) => { return state.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); }); todos.update(updateFn); } // App.svelte import { markTodoAsDone } from './todos.store'; function onDone(event) { const name = event.detail; markTodoAsDone(name); } 

Abonnement zur Statusänderung


Um festzustellen, dass sich der Wert im Repository geändert hat, können Sie die Methode subscribe verwenden. Beachten Sie, dass das Repository kein observable Objekt ist, sondern eine ähnliche Schnittstelle bietet.


 const subscription = todos.subscribe(console.log); subscription(); //     

Observables


Wenn dieser Teil Sie am meisten aufgeregt hat, dann beeile ich mich, dass Svelte vor nicht allzu langer Zeit RxJS-Unterstützung hinzugefügt und Observable for ECMAScript eingeführt wurde.


Als Angular-Entwickler bin ich bereits daran gewöhnt, mit reaktiver Programmierung zu arbeiten, und das Fehlen eines Gegenstücks zur asynchronen Pipe wäre äußerst unpraktisch. Aber auch hier hat mich Svelte überrascht.


Schauen wir uns ein Beispiel für die Zusammenarbeit dieser Tools an: Zeigen Sie eine Liste der Repositorys in Github an, die unter dem Schlüsselwort "Svelte" .


Sie können den folgenden Code kopieren und direkt in der REPL ausführen:


 <script> import rx from "https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"; const { pluck, startWith } = rx.operators; const ajax = rx.ajax.ajax; const URL = `https://api.github.com/search/repositories?q=Svelte`; const repos$ = ajax(URL).pipe( pluck("response"), pluck("items"), startWith([]) ); </script> {#each $repos$ as repo} <div> <a href="{repo.url}">{repo.name}</a> </div> {/each} <!--   Angular: <div *ngFor="let repo of (repos$ | async)> <a [attr.href]="{{ repo.url }}">{{ repo.name }}</a> </div> --> 

Fügen Sie einfach das $ -Symbol zum Namen der beobachtbaren Variablen repos$ und Svelte zeigt automatisch den Inhalt an.


Meine Wunschliste für Svelte


Typoskript-Unterstützung


Als Typescript-Enthusiast kann ich mir nur die Möglichkeit wünschen, Typen in Svelte zu verwenden. Ich bin so daran gewöhnt, dass ich manchmal mitgerissen werde und Typen in meinem Code anordne, die ich dann entfernen muss. Ich hoffe wirklich, dass Svelte bald Unterstützung für Typescript hinzufügen wird. Ich denke, dieser Artikel wird auf der Wunschliste von jedem stehen, der Svelte mit Erfahrung mit Angular verwenden möchte.


Vereinbarungen und Richtlinien


Das Rendern in der Darstellung einer Variablen aus dem <script> -Block ist eine sehr leistungsstarke Funktion des Frameworks, kann aber meiner Meinung nach zu Code-Unordnung führen. Ich hoffe, dass die Svelte-Community eine Reihe von Konventionen und Richtlinien durcharbeitet, um Entwicklern beim Schreiben von sauberem und verständlichem Komponentencode zu helfen.


Unterstützung durch die Gemeinschaft


Svelte ist ein grandioses Projekt, das mit zunehmendem Aufwand der Community beim Schreiben von Paketen, Handbüchern, Blog-Artikeln und vielem mehr von Drittanbietern zu einem anerkannten Werkzeug in der erstaunlichen Welt der Frontend-Entwicklung werden kann, die wir heute haben.


Abschließend


Trotz der Tatsache, dass ich kein Fan der vorherigen Version des Frameworks war, machte Svelte 3 einen guten Eindruck auf mich. Es ist einfach, klein, kann aber viel. Es ist so anders als alles andere, dass es mich an die Aufregung erinnerte, die ich erlebte, als ich von jQuery zu Angular wechselte.


Unabhängig davon, welches Framework Sie gerade verwenden, dauert das Erlernen von Svelte wahrscheinlich nur einige Stunden. Sobald Sie die Grundlagen gelernt und die Unterschiede zu dem, was Sie an Schreiben gewöhnt sind, verstanden haben, wird die Arbeit mit Svelte sehr einfach.


Im russischsprachigen Telegrammkanal @sveltejs finden Sie sicherlich Entwickler, die Erfahrung mit verschiedenen Frameworks haben und bereit sind, ihre Geschichten, Gedanken und Tipps zu Svelte zu teilen.

Source: https://habr.com/ru/post/de467091/


All Articles