In jüngster Zeit gewinnt der Trend zum
„Verschwinden von Frameworks“ an Popularität, dessen Lokomotive zweifellos als
SvelteJS bezeichnet werden kann - ein Buildtime-Framework und ein Vanilla-Javascript-Compiler.
Trotz der Tatsache, dass Svelte konzeptionell sehr einfach und noch einfacher zu verwenden ist, fragen sich viele Entwickler, was das Killer-Feature dieses Frameworks und den gesamten Ansatz ist. Warum ist dies kein „weiteres Javascript-Framework“?
In diesem Artikel werde ich über eine der vielen Svelte-Supermächte sprechen, die Ihr Leben ernsthaft erleichtern können.
Lassen Sie es uns herausfinden, aber zuerst werde ich Ihnen eine Legende erzählen ...
Die Legende vom Machtrahmen
Einige Frameworks wurden von den Jungs von Google und Facebook erstellt, andere - coole Typen, aber alle unter der engen "Aufmerksamkeit" von Rich Harris .
Neun Frameworks wurden für Menschen geschaffen, sieben scheinen für Zwerge zu sein. Drei weitere Gerüste (reagieren, vue, eckig) waren für Elfen.
Nachdem Rich Harris die Frameworks erstellt und in Tausenden von Projekten implementiert hatte, erstellte er persönlich und heimlich ein Framework ...
Ein Rahmen, um sie alle zu regieren,
Ein Rahmen, um sie zu finden,
Ein Rahmen, um sie alle zu bringen
Und binden sie zusammen.
- Der Herr der Rahmenbedingungen
Das Problem
Ich bin sicher, dass viele von Ihnen, die sich ernsthaft und lange mit Front-End-Entwicklung beschäftigt haben, wiederholt mit dem Problem konfrontiert waren, Tools für Ihr aktuelles und / oder nächstes Projekt auszuwählen.
Die Vielfalt aller Arten von Paketen, Dienstprogrammen, Walen, Frameworks, Bibliotheken und anderen Lösungen ist so groß wie nie zuvor. Und vor allem beschleunigt sich all diese Bewegung weiter.
All dies gilt auf die eine oder andere Weise für die Wahl des Rahmens. Wahrscheinlich nicht falsch, wenn ich davon ausgehe, dass nur wenige moderne Teams und Unternehmen neue Projekte starten, ohne ein js-Framework zu verwenden. Natürlich, wenn es um moderne Webanwendungen geht, nicht nur um Websites. Und alles wäre in Ordnung, wenn so viel in Ihrem Projekt nicht davon abhängen würde.
Überzeugen Sie sich selbst, dass die Zusammensetzung und die Eigenschaften des Teams weitgehend von dem von Ihnen gewählten Rahmen abhängen, und der gesamte Prozess der Jagd hängt sicherlich davon ab. Manchmal hängen das Budget und die Zeitpläne sogar davon ab. Kurz brrr.
Die eigentlichen Probleme beginnen jedoch, wenn Sie irgendwo in der Mitte des Projekts feststellen, dass Sie eine falsche Wahl getroffen haben. Etwas wuchs nicht zusammen und drehte sich. Das Framework benötigte etwas mehr Zeit, um es zu meistern, ein etwas größeres Team, erwies sich als etwas weniger schnell, etwas nicht geeignet für Ihre Ziele oder Ihren Entwicklungsstil usw. Und am wichtigsten ist, dass Ihr Projekt jetzt zu 100% an dieses Framework gebunden ist und Sie es nicht einfach auf etwas anderes übertragen können.
Noch offensiver, wenn Sie das Projekt dennoch mit Erfolg abgeschlossen haben, verstehen Sie, dass Sie im Allgemeinen nicht sehr glücklich sind. Und wahrscheinlich möchten wir das nächste Projekt nicht im selben Framework schreiben. Alle diese „wiederverwendeten“ Lösungen, die wir anstreben, können in die Pipe geworfen werden.
Zum Teufel funktioniert es mit einem Geschäftscode, der eine bestimmte Geschäftsaufgabe implementiert. Aber Sie haben so ein cooles "% füge dein% mit Blackjack und Mädchen mit geringer sozialer Verantwortung ein" geschrieben, und du wolltest es in deinem nächsten Projekt verwenden, aber diese Infektion ist eng mit dem aktuellen Rahmen verbunden, von dem aus du bereits eine Art betrachtest.
Eine weitere Variante des gleichen Problems: Stellen Sie sich vor, Sie sind ein großes Unternehmen wie Yandex. Sie haben eine Vielzahl von Projekten, von denen einige nur einigen Mitarbeitern bekannt sind, und jedes Projekt hat bereits alles erlebt, was ich oben beschrieben habe. Das Problem ist, dass all diese Projekte die verschiedenen Frameworks hassen, die sie ursprünglich ausgewählt haben.
Und hier ist Ihre wunderbare Führung, die sich entschlossen hat, mit Google Material Design zu konkurrieren und Sie auf einen Kreuzzug zu den vielfältigen Schnittstellen Ihrer Projekte zu schicken, um sie auf einen gemeinsamen Nenner zu bringen. Schlaue Designer zeichnen bereits neue Schaltflächen und Selektoren und kritzeln Tausende von Seiten mit Richtlinien für das neue einzelne UI-Kit Ihrer Komponenten. Hurra Kameraden!
Nicht das Leben, sondern ein Märchen, oder? Es bleibt nur zu überlegen, wie Sie all diese neuen Komponenten in all den Projekten abrufen können, die Sie bereits in alle möglichen Frameworks geschrieben haben. Wenn es wirklich viel Zeit und Geld gibt und es einen ästhetischen Wunsch gibt und vor allem den Glauben, dass "alles vereinheitlicht werden muss", dann können Sie ein paar Dutzend Teams einsetzen, um all dies erneut zu schreiben, zum Beispiel auf React. Das ist richtig, denn die langweilige Scheiße, über die Sie in den letzten 2-3 Jahren geschrieben haben, ist moralisch bereits veraltet, aber React wird für immer sein. Gut, gut)
Es gibt noch einen anderen Weg. Sie können ein wunderbares neues UI-Kit auf einem Framework schreiben, sozusagen eine Bibliothek wiederverwendbarer Komponenten erstellen und dieses UI-Kit dann einfach in all Ihren Projekten verwenden. Klingt cool? Natürlich, aber es bleibt ein Problem - die Laufzeit.
Wenn Ihr Projekt in Angular (~ 500 KB) geschrieben ist und Sie sich entschieden haben, ein UI-Kit in React (~ 98 KB) zu schreiben, ziehen Sie jedes Projekt auf ein Framework, ein anderes Framework und selbst mit einer Reihe von Abhängigkeiten ist das UI-Kit selbst direkt Nehmen wir an, es sieht nicht optimal aus.
Lösung
Um uns zu helfen, kommen die sehr "verschwindenden" Frameworks ohne Laufzeit. Die Hauptbedingung hierbei ist, dass sie hinsichtlich ihres Ökosystems so isoliert wie möglich sind und über externe Integrationsmechanismen und entsprechende APIs verfügen.
Ein gutes Beispiel für ein solches Framework ist SvelteJS, über das bereits
einige Artikel über Habré geschrieben wurden .
Stellen Sie sich also die Situation vor, in der wir eine Anwendung auf React haben. Vielleicht haben wir es satt und wollen es loswerden, aber alles auf einmal umzuschreiben ist ein unzulässiger Luxus. Oder einige Teile der Anwendung müssen verbessert oder umgestaltet werden. Nun, oder wir haben beschlossen, eine einzelne Komponentenbibliothek zu erstellen, und jetzt werden wir alle Komponenten in Svelte schreiben und in allen Projekten verwenden. Präsentiert? Ja, natürlich nicht, niemand hat eine solche Fantasie. Schauen wir uns ein reales Beispiel an.

HaftungsausschlussIch möchte Sie sofort darauf aufmerksam machen, dass ich kein React-Entwickler bin und das letzte Mal, dass ich React im Jahr 2015 "gefühlt" habe. Daher nehme ich an, dass die Art und Weise, wie ich einen Teil des React-Beispiels geschrieben habe, die Gefühle glaubender Reaktoren verletzen kann. Es tut mir leid, nicht streng zu urteilen, zumal sich die Bedeutung des Artikels nicht ändert.
Die Aufgabe besteht also darin, die vorgefertigte Svelte-Komponente in die React-Anwendung zu implementieren, ohne die Komponente selbst zu ändern und ohne zusätzliche Laufzeit in die Anwendung zu packen. Zum Beispiel nehme ich die Komponente, die nach GitHub-Benutzern sucht, die ich für den vorherigen Artikel
"So suchen Sie auf GitHub nach Benutzern ohne React + RxJS 6 + Recompose" geschrieben habe .
Der Code für diese Komponente kann in der
REPL und der Beispielcode aus diesem Artikel im
Repository angezeigt werden.
Erstellen Sie eine Reaktions-App
Erstellen Sie zunächst ein neues React-Projekt mit dem De-facto-Standardtool -
create-react-app :
npx create-react-app my-app cd my-app npm start
Ok, wenn Sie zum 3000. Port gehen, scheint es zu funktionieren.
Passen Sie Svelte an
Wenn Sie nichts über Svelte wissen, werde ich dies im Kontext der Svelte-Aufgabe sagen. Dies ist nur ein weiterer Schritt Ihres Sammlers (Webpack / Rollup / Gupl / Grunt / etc), mit dem Sie Komponenten im SFC-Format schreiben und in Vanille kompilieren können Javascript.
In der Svelte-Community wird Rollup bevorzugt, was nicht verwunderlich ist, da sie einen Autor haben - Rich Harris. Da CRA jedoch Webpack verwendet, werden wir Svelte damit konfigurieren. Dazu müssen Sie zuerst die Webpack-Konfigurationen von React-Skripten in das Projekt übertragen, damit wir sie ändern können. Dies erfolgt mit dem eingebauten Befehl:
npm run eject
Soweit ich weiß, ist dies kein koscherer Ansatz, aber zum Beispiel ist dies die bequemste Option.
Nachdem sich die Webpack-Konfigurationen im Stammverzeichnis des Projekts befinden, können Sie Svelte installieren:
npm i --save-dev svelte svelte-loader
Achten Sie auf das
Flag --save-dev. Denken Sie daran, dass es keine Laufzeit gibt.))))
Als letzten Schliff müssen Sie den entsprechenden Lader in den Konfigurationen anschließen:
{ test: /\.svelte$/, use: { loader: 'svelte-loader', } },
Im Allgemeinen ist es in der Svelte-Community üblich, Komponentendateien mit der Erweiterung
.html zu schreiben, da die Svelte-Komponente eine gültige HTML-Datei ist. Um mögliche Kollisionen zu vermeiden, ist es in einigen Fällen besser, das benutzerdefinierte
.svelte- Dateiformat zu
verwenden .
Nun haben wir alle im Projekt
enthaltenen .svelte- Dateien von diesem Loader abgefangen und von Svelte kompiliert.
Schreiben einer schlanken Komponente
Zunächst ist es beispielsweise besser, den Code-Editor so zu konfigurieren, dass er die Hervorhebung der HTML-Syntax auf Dateien mit der entsprechenden Erweiterung anwendet. So etwas wird in VS Code gemacht:
"files.associations": { "*.svelte": "html" }
Erstellen Sie nun einen Ordner ./src/svelte_components/ und dort den Ordner der Komponente selbst. Danach übertragen wir einfach alle Dateien aus dem
REPL-Beispiel in diesen Ordner, geben ihnen gleichzeitig die neue Erweiterung
.svelte und rufen die Datei App.html Widget.svelte auf.
Das Ergebnis sollte ungefähr so aussehen:
An derselben Stelle erstellen wir die Datei index.js, in der wir den Integrationscode Svelte and React haben.
Integrieren
Vielleicht möchten Sie jetzt wissen, was Magie ist? Die Magie ist, dass wir bereits die ganze Magie gemacht haben. Magisch, nicht wahr?
Im Ernst, jetzt können wir die Svelte-Komponenten in unserer React-Anwendung als ganz normale JS-Konstruktoren verwenden, was bedeutet, dass sich der Integrationscode mit Svelte nicht von der Integration mit anderen eigenständigen Komponenten unterscheidet. Die React-Dokumentation enthält sogar einen entsprechenden Abschnitt:
Integration in andere Bibliotheken .
Der Integrationscode kann folgendermaßen aussehen:
import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { const { username } = this.props; this.widget = new Widget({ target: this.el, data: { username } }); } componentWillUnmount() { this.widget.destroy(); } render() { return ( <div ref={el => this.el = el}></div> ); } }
Insgesamt haben wir den Code unserer komplexen Svelte-Komponente einfach in eine sehr einfache React-Komponente verpackt, die einfach eine neue Instanz der Svelte-Komponente erstellt und beim Erstellen das Mount-Element und die Daten von den Requisiten übergibt. Außerdem vergessen wir nicht, die Svelte-Komponente im
componentWillUnmount- Hook zu deinstallieren.
Das einzige, was wir noch nicht getan haben, ist, dass sie die Werte des Komponentenstatus nicht synchronisiert haben. Das heißt, wenn die übergeordnete Komponente andere Requisiten in die Wrapper-Komponente geworfen hat, sollten sie auf die Svelte-Komponente angewendet werden. Wenn umgekehrt die Daten innerhalb der Svelte-Komponente geändert wurden, müssen sie zurückgesetzt werden.
Zu diesem Zweck gehen wir davon aus, dass die übergeordnete React-Komponente den onChange-Rückruf überträgt, den wir abrufen sollten, wenn Änderungen im Inneren auftreten, und dass wir Änderungen an den Requisiten der Wrapper-
Komponente im
componentWillReceiveProps- Hook erwarten. Lass es uns tun:
componentDidMount() { ... this.widget.on('state', ({ current: { username }, changed }) => { if (changed.username) { this.props.onChange({ username }); } }); } componentWillReceiveProps({ username }) { this.widget.set({ username }); }
Hier haben wir das integrierte Statusereignis verwendet, das jedes Mal ausgelöst wird, wenn sich der
Status der Svelte-Komponente ändert. Ein Objekt, das den aktuellen Status der Komponente (
aktuell ), den vorherigen Status (
zuvor ) und die Liste der geänderten Eigenschaften (
geändert ) enthält, wird an den Rückruf übertragen. Dementsprechend prüfen wir einfach, ob der Benutzername geändert wurde, und rufen gegebenenfalls den
Rückruf onChange auf.
Im
KomponentenWillReceiveProps- Hook legen wir den neuen
Benutzernamen mithilfe der integrierten
set () -Methode fest.
Zusätzlich zu den integrierten Komponenten können Svelte-Komponenten benutzerdefinierte Ereignisse und Methoden implementieren. Mit diesen netten Funktionen können Sie die Benutzeroberfläche der Komponente beschreiben und die Kommunikation mit der "Außenwelt" bequem organisieren.
Verwenden Sie
Versuchen wir nun, unser Widget direkt in der React-Anwendung zu verwenden. Bearbeiten Sie dazu die vom Starter generierte Datei App.js:
import React, { Component } from 'react'; import './App.css'; import GithubWidget from './svelte_components/GithubWidget'; class App extends Component { constructor() { super(); this.state = { username: '' }; } handleChange = (state) => { this.setState({ ...state }); } render() { return ( <div className="App"> <header className="App-header"> <h1>Github Widget for: {this.state.username}</h1> <GithubWidget onChange={this.handleChange} username={this.state.username} /> </header> </div> ); } } export default App;
Kurz gesagt, wir verwenden es als reguläre React-Komponente. Und als Ergebnis erhalten wir:
Schon nicht schlecht, oder?) Bitte beachten Sie, dass der Benutzername, den wir in das Textfeld des Widgets eingeben, sofort zur React-Anwendung springt.
Wir werden abschließen
Lassen Sie uns nun unserem Widget beibringen, nicht nur die GitHub-Benutzerkarte, sondern auch die Repository-Karte zu suchen und anzuzeigen.
Zunächst müssen Sie eine neue Komponente Repo.svelte erstellen, die die Repository-Karte zeichnet. Der Einfachheit halber habe ich nur die Vorlage und die Stile von User.svelte kopiert und an die Repository-Daten angepasst. Theoretisch ist dies jedoch eine separate Komponente.
Als Nächstes müssen Sie die Steuerungskomponente Widget.svelte lernen, um diese beiden Kartentypen im laufenden Betrieb zu wechseln. Außerdem müssen Sie ihm beibringen, verschiedene Anforderungen für den Benutzer und das Repository abzurufen.
Wir werden ein Feld für die Eingabe verwenden und den Datentyp durch das Vorhandensein von "/" im Wert bestimmen. Das heißt, wenn Sie nach dem Benutzer suchen müssen, geben Sie seinen Benutzernamen und, wenn das Repository, den Benutzernamen des Benutzers, dann "/" und den Namen des Repositorys ein.
Auf den ersten Blick sieht es ziemlich verwirrt aus, aber auf Svelte wird die Lösung buchstäblich 5-6 Codezeilen benötigen. Lassen Sie uns zunächst eine neue Komponente und eine neue API-Methode verbinden, die wir in debounce einschließen:
... import Repo from './Repo.svelte'; ... import { getUserCard, getRepoCard } from './api.js'; ... const getRepo = debounce(getRepoCard, 1000);
Erstellen Sie als Nächstes eine berechnete Eigenschaft, die bestimmt, welcher Kartentyp angezeigt werden soll:
computed: { ... repo: ({ username }) => username.includes('/'), ... }
Fügen Sie nun den API-Schalter für Anforderungen hinzu:
computed: { ... card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username), ... }
Und schließlich das Wechseln der Kartenkomponenten je nach Typ:
computed: { ... Card: ({ repo }) => repo ? Repo : User, ... }
Um Komponenten dynamisch zu ersetzen, müssen wir außerdem ein spezielles Svelte-Tag verwenden, das die Komponente zeichnet, deren Wert an
dieses Attribut übergeben wird:
<svelte:component this={Card} {...card} />
Es funktioniert. Hast du es bemerkt? Wir schreiben bereits über Svelte in der React-Anwendung! )))
Lassen Sie uns nun unserem Widget beibringen, das Eingabefeld auszublenden, und versuchen, den Benutzernamen nicht im Widget, sondern in der React-Anwendung einzugeben. Ist es wichtig, wie unsere Geschäftslogik diesen Wert erhält?
Wir führen eine neue
Sucheigenschaft ein, deren Standardwert false ist. Abhängig von dieser Eigenschaft wird das Eingabefeld angezeigt oder nicht angezeigt. Dementsprechend wird standardmäßig kein Feld angezeigt.
{#if search} <input bind:value=username placeholder="username or username/repo"> {/if} ... <script> export default { ... data() { return { username: '', search: false }; }, ... }; </script>
Jetzt erstellen wir in App.js ein Eingabefeld auf der React-Seite der Anwendung und schreiben die entsprechende Verarbeitung für das Eingabeereignis:
... handleUsername = (e) => { this.setState({ username: e.target.value }); } ... <h1>Github Widget for: {this.state.username}</h1> <input value={this.state.username} onChange={this.handleUsername} className="Username" placeholder="username or username/repo" />
Und auch kopieren Sie es in den Ordner mit dem Widget hier ist so ein SVG-Spinner auf Svelte:
<svg height={size} width={size} style="animation-duration:{speed}ms;" class="svelte-spinner" viewbox="0 0 32 32" > <circle role="presentation" cx="16" cy="16" r={radius} stroke={color} fill="none" stroke-width={thickness} stroke-dasharray="{dash},100" stroke-linecap="round" /> </svg> <script> export default { data() { return { size: 25, speed: 750, color: 'rgba(0,0,0,0.4)', thickness: 2, gap: 40, radius: 10 }; }, computed: { dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100 } }; </script> <style> .svelte-spinner { transition-property: transform; animation-name: svelte-spinner_infinite-spin; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes svelte-spinner_infinite-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style>
Und wir werden es im Widget anwenden, damit es sehr schön ist:
... {#await card} <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" /> {:then card} ...
Meiner Meinung nach stellte sich heraus, dass es nicht sehr schlecht war:
Der Zylinder mit schwarzem Hintergrund und Eingabefeld ist eine React-Anwendung, der weiße Block darunter ist ein Svelte-Widget. Das sind die Kuchen. )))
→
RepositorySchlussfolgerungen
Svelte ist ein großartiges Tool für die Entwicklung moderner komponentenbasierter Webanwendungen. Darüber hinaus können Sie schnell und bequem wiederverwendbare eigenständige UI-Komponenten und Widgets darauf schreiben, die in allen Webanwendungen verwendet werden können, auch in Verbindung mit anderen Frameworks. Es eignet sich auch hervorragend für
Mikrofronts .
Svelte ist perfekt für Sie, wenn:
- Sie möchten ein neues Projekt starten und wissen nicht, welches Framework Sie dafür wählen sollen.
- Sie haben ein Projekt, es funktioniert und es ist besser, es nicht zu berühren. Neue Komponenten und Module können Sie in Svelte schreiben und nahtlos in vorhandenen Code integrieren.
- Sie haben bereits ein Projekt, aber es ist teilweise oder vollständig veraltet und / oder erfordert ernsthafte Umgestaltungen bis hin zu einer vollständigen Neufassung. Sie können beginnen, es in Teilen neu zu schreiben. In diesem Fall müssen Sie keine komplexen Konfigurationen erstellen. Sie nehmen einfach eine Komponente, schreiben sie in Svelte um und verpacken die neue Komponente mit der alten. Dem Rest der Anwendung sind die Änderungen jedoch nicht einmal bekannt.
- Sie haben mehrere Projekte auf unterschiedlichen Codebasen und möchten gleichzeitig ein einziges UI-Kit haben und es in einem dieser Projekte verwenden. Schreiben Sie ein UI-Kit auf Svelte und verwenden Sie es überall. Es ist schön.
Möchten Sie weitere interessante Fälle herausfinden? Treten Sie unserem
Telegrammkanal bei !
UPDATE: danke
justboris für die richtige
frage . Fortsetzung des Beispiels:
import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { ... this.widget = new Widget({ target: this.el, data: { username }, slots: { default: this.slot } }); ... } ... render() { return ( <div ref={el => this.el = el}> <div ref={el => this.slot = el}> {this.props.children} </div> </div> ); } }
<GithubWidget onChange={this.handleChange} username={this.state.username}> <p>Hello world</p> </GithubWidget>