Verwenden von Spielautomaten in Vue.js

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Slots sind ein leistungsstarkes Tool zum Erstellen wiederverwendbarer Komponenten in Vue.js, obwohl sie nicht die am einfachsten zu verstehende Funktion sind. Werfen wir einen Blick auf die Verwendung von Slots und einige Beispiele, wie sie in Ihren Vue-Anwendungen verwendet werden können.

Mit der jüngsten Veröffentlichung von Vue 2.6 wurde die Syntax für die Verwendung von Slots prägnanter gestaltet. Diese Änderung an Slots hat mein Interesse daran geweckt, die potenzielle Leistungsfähigkeit von Slots zu entdecken, um unseren Vue-basierten Projekten Wiederverwendbarkeit, neue Funktionen und eine klarere Lesbarkeit zu bieten. Was können Slots wirklich?

Wenn Sie neu bei Vue sind oder die Änderungen von Version 2.6 noch nicht gesehen haben, lesen Sie weiter. Die wahrscheinlich beste Quelle, um etwas über Spielautomaten zu lernen, ist Vues eigene Dokumentation, aber ich werde versuchen, hier einen Überblick zu geben.

Was sind Spielautomaten?

Slots sind ein Mechanismus für Vue-Komponenten, mit dem Sie Ihre Komponenten auf andere Weise als die strikte Eltern-Kind-Beziehung zusammenstellen können. Slots bieten Ihnen die Möglichkeit, Inhalte an neuen Orten zu platzieren oder Komponenten generischer zu gestalten. Der beste Weg, sie zu verstehen, ist, sie in Aktion zu sehen. Beginnen wir mit einem einfachen Beispiel:

 // frame.vue <template> <div class="frame"> <slot></slot> </div> </template>

Diese Komponente hat einen Wrapper div . Nehmen wir an, dass div dazu da ist, einen stilistischen Rahmen um seinen Inhalt zu schaffen. Diese Komponente kann generisch verwendet werden, um einen Rahmen um jeden gewünschten Inhalt zu legen. Mal sehen, wie es aussieht, es zu benutzen. Die frame bezieht sich hier auf die Komponente, die wir gerade oben gemacht haben.

 // app.vue <template> <frame><img src="an-image.jpg"></frame> </template>

Der Inhalt, der sich zwischen den öffnenden und schließenden frame Tags befindet, wird in die frame Komponente eingefügt, in der sich der slot befindet, und ersetzt die slot Tags. Dies ist die einfachste Art, dies zu tun. Sie können auch Standardinhalte für einen Slot angeben, indem Sie sie einfach ausfüllen:

 // frame.vue <template> <div class="frame"> <slot>This is the default content if nothing gets specified to go here</slot> </div> </template>

Wenn wir es jetzt also stattdessen so verwenden:

 // app.vue <template> <frame /> </template>

Der Standardtext „Dies ist der Standardinhalt, wenn hier nichts angegeben wird“ wird angezeigt, aber wenn wir ihn wie zuvor verwenden, wird der Standardtext durch das img -Tag überschrieben.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Mehrere/benannte Slots

Sie können einer Komponente mehrere Steckplätze hinzufügen, aber wenn Sie dies tun, müssen alle bis auf einen einen Namen haben. Wenn es einen ohne Namen gibt, ist es der Standardsteckplatz. So erstellen Sie mehrere Slots:

 // titled-frame.vue <template> <div class="frame"> <header><h2><slot name="header">Title</slot></h2></header> <slot>This is the default content if nothing gets specified to go here</slot> </div> </template>

Wir haben den gleichen Standard-Slot beibehalten, aber dieses Mal haben wir einen Slot namens header hinzugefügt, wo Sie einen Titel eingeben können. Sie verwenden es wie folgt:

 // app.vue <template> <titled-frame> <template v-slot:header> <!-- The code below goes into the header slot --> My Image's Title </template> <!-- The code below goes into the default slot --> <img src="an-image.jpg"> </titled-frame> </template>

Wenn wir wie zuvor Inhalte zum Standard titled-frame ein. Um jedoch Inhalt zu einem benannten Slot hinzuzufügen, mussten wir den Code in ein template -Tag mit einer v-slot Direktive einschließen. Sie fügen einen Doppelpunkt ( : ) nach v-slot hinzu und schreiben dann den Namen des Slots, an den der Inhalt weitergeleitet werden soll. Beachten Sie, dass v-slot neu in Vue 2.6 ist. Wenn Sie also eine ältere Version verwenden, müssen Sie die Dokumentation zur veralteten Slot-Syntax lesen.

Scoped-Slots

Eine weitere Sache, die Sie wissen müssen, ist, dass Slots Daten/Funktionen an ihre Kinder weitergeben können. Um dies zu demonstrieren, benötigen wir eine völlig andere Beispielkomponente mit Slots, die noch ausgeklügelter ist als die vorherige: Kopieren wir das Beispiel aus der Dokumentation, indem wir eine Komponente erstellen, die die Daten über den aktuellen Benutzer an ihre Slots liefert:

 // current-user.vue <template> <span> <slot v-bind:user="user"> {{ user.lastName }} </slot> </span> </template> <script> export default { data () { return { user: ... } } } </script>

Diese Komponente hat eine Eigenschaft namens user mit Details über den Benutzer. Standardmäßig zeigt die Komponente den Nachnamen des Benutzers an, aber beachten Sie, dass sie v-bind verwendet, um die Benutzerdaten an den Slot zu binden. Damit können wir diese Komponente verwenden, um die Benutzerdaten an ihren Nachkommen weiterzugeben:

 // app.vue <template> <current-user> <template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template> </current-user> </template>

Um Zugriff auf die an den Slot übergebenen Daten zu erhalten, geben wir den Namen der Bereichsvariablen mit dem Wert der v-slot Direktive an.

Hier sind ein paar Anmerkungen zu machen:

  • Wir haben den Namen von default angegeben, obwohl wir dies für den Standard-Slot nicht brauchen. Stattdessen könnten wir einfach v-slot="slotProps" .
  • Sie müssen slotProps nicht als Namen verwenden. Du kannst es nennen, wie du willst.
  • Wenn Sie nur einen Standard-Slot verwenden, können Sie dieses innere template -Tag überspringen und die v-slot Direktive direkt auf das current-user Tag setzen.
  • Sie können die Objektdestrukturierung verwenden, um direkte Verweise auf die bereichsbezogenen Steckplatzdaten zu erstellen, anstatt einen einzelnen Variablennamen zu verwenden. Mit anderen Worten, Sie können v-slot="{user}" anstelle von v-slot="slotProps" und dann können Sie user direkt anstelle von slotProps.user .

Unter Berücksichtigung dieser Notizen kann das obige Beispiel wie folgt umgeschrieben werden:

 // app.vue <template> <current-user v-slot="{user}"> {{ user.firstName }} </current-user> </template>

Noch ein paar Dinge, die Sie beachten sollten:

  • Sie können mehr als einen Wert mit v-bind Anweisungen binden. In dem Beispiel hätte ich also mehr tun können als nur user .
  • Sie können Funktionen auch an bereichsbezogene Slots übergeben. Viele Bibliotheken verwenden dies, um wiederverwendbare funktionale Komponenten bereitzustellen, wie Sie später sehen werden.
  • v-slot hat den Aliasnamen # . Anstatt also v-slot:header="data" zu schreiben, können Sie #header="data" schreiben. Sie können auch einfach #header anstelle von v-slot:header angeben, wenn Sie keine bereichsbezogenen Slots verwenden. Wie bei Standard-Slots müssen Sie den Namen von default angeben, wenn Sie den Alias ​​verwenden. Mit anderen Worten, Sie müssen #default="data" anstelle von #="data" schreiben.

Es gibt noch ein paar kleinere Punkte, die Sie aus der Dokumentation lernen können, aber das sollte ausreichen, um Ihnen zu helfen, zu verstehen, worüber wir im Rest dieses Artikels sprechen.

Was können Sie mit Spielautomaten tun?

Spielautomaten wurden nicht für einen einzigen Zweck gebaut, oder zumindest, wenn sie es wären, haben sie sich weit über die ursprüngliche Absicht hinaus entwickelt, ein Kraftpaket für viele verschiedene Dinge zu sein.

Wiederverwendbare Muster

Komponenten wurden immer so entworfen, dass sie wiederverwendet werden können, aber einige Muster lassen sich nicht mit einer einzelnen „normalen“ Komponente durchsetzen, da die Anzahl der props , die Sie benötigen, um sie anzupassen, übermäßig groß sein kann oder Sie müssten große Teile des Inhalts und möglicherweise andere Komponenten durch die props . Slots können verwendet werden, um den „äußeren“ Teil des Musters einzuschließen und zu ermöglichen, dass andere HTML- und/oder Komponenten darin platziert werden, um den „inneren“ Teil anzupassen, sodass die Komponente mit Slots das Muster und die injizierten Komponenten definieren kann Slots einzigartig sein.

Beginnen wir für unser erstes Beispiel mit etwas Einfachem: einer Schaltfläche. Stellen Sie sich vor, Sie und Ihr Team verwenden Bootstrap*. Bei Bootstrap werden Ihre Schaltflächen häufig mit der Basisklasse „btn“ und einer Klasse, die die Farbe angibt, z. B. „btn-primary“, umreift. Sie können auch eine Größenklasse wie „btn-lg“ hinzufügen.

* Ich ermutige Sie weder noch entmutige Sie, dies zu tun, ich brauchte nur etwas für mein Beispiel und es ist ziemlich bekannt.

Nehmen wir nun der Einfachheit halber an, dass Ihre App/Site immer btn-primary und btn-lg verwendet. Sie wollen nicht immer alle drei Klassen auf Ihre Buttons schreiben müssen, oder vielleicht vertrauen Sie einem Anfänger nicht, dass er sich daran erinnert, alle drei zu machen. In diesem Fall können Sie eine Komponente erstellen, die automatisch alle drei dieser Klassen enthält, aber wie ermöglichen Sie die Anpassung des Inhalts? Eine prop ist nicht praktisch, da ein button -Tag alle Arten von HTML enthalten darf, also sollten wir einen Slot verwenden.

 <!-- my-button.vue --> <template> <button class="btn btn-primary btn-lg"> <slot>Click Me!</slot> </button> </template>

Jetzt können wir es überall mit beliebigen Inhalten verwenden:

 <!-- somewhere else, using my-button.vue --> <template> <my-button> <img src="/img/awesome-icon.jpg"> SMASH THIS BUTTON TO BECOME AWESOME FOR ONLY $500!!! </my-button> </template>

Natürlich können Sie etwas viel Größeres als einen Knopf verwenden. Um bei Bootstrap zu bleiben, schauen wir uns ein Modal oder zumindest den HTML-Teil an. Ich werde nicht auf die Funktionalität eingehen ... noch nicht.

 <!-- my-modal.vue --> <template> <div class="modal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <slot name="header"></slot> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <slot name="body"></slot> </div> <div class="modal-footer"> <slot name="footer"></slot> </div> </div> </div> </div> </template>

Nun, lassen Sie uns das verwenden:

 <!-- somewhere else, using my-modal.vue --> <template> <my-modal> <template #header><!-- using the shorthand for `v-slot` --> <h5>Awesome Interruption!</h5> </template> <template #body> <p>We interrupt your use of our application to let you know that this application is awesome and you should continue using it every day for the rest of your life!</p> </template> <template #footer> <em>Now back to your regularly scheduled app usage</em> </template> </my-modal> </template>

Der obige Anwendungsfall für Slots ist offensichtlich sehr nützlich, kann aber noch mehr.

Funktionalität wiederverwenden

Bei Vue-Komponenten dreht sich nicht alles um HTML und CSS. Sie werden mit JavaScript erstellt, also geht es auch um Funktionalität. Slots können nützlich sein, um Funktionen einmal zu erstellen und an mehreren Stellen zu verwenden. Kehren wir zu unserem modalen Beispiel zurück und fügen eine Funktion hinzu, die das Modal schließt:

 <!-- my-modal.vue --> <template> <div class="modal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <slot name="header"></slot> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <slot name="body"></slot> </div> <div class="modal-footer"> <!-- using `v-bind` shorthand to pass the `closeModal` method to the component that will be in this slot --> <slot name="footer" :closeModal="closeModal"></slot> </div> </div> </div> </div> </template> <script> export default { //... methods: { closeModal () { // Do what needs to be done to close the modal... and maybe remove it from the DOM } } } </script>

Wenn Sie diese Komponente jetzt verwenden, können Sie der Fußzeile eine Schaltfläche hinzufügen, die das Modal schließen kann. Normalerweise könnten Sie im Fall eines Bootstrap-Modals einfach data-dismiss="modal" zu einer Schaltfläche hinzufügen, aber wir möchten Bootstrap-spezifische Dinge vor den Komponenten verbergen, die in diese modale Komponente eingefügt werden. Also übergeben wir ihnen eine Funktion, die sie aufrufen können, und sie wissen nichts über die Beteiligung von Bootstrap:

 <!-- somewhere else, using my-modal.vue --> <template> <my-modal> <template #header><!-- using the shorthand for `v-slot` --> <h5>Awesome Interruption!</h5> </template> <template #body> <p>We interrupt your use of our application to let you know that this application is awesome and you should continue using it every day for the rest of your life!</p> </template> <!-- pull in `closeModal` and use it in a button's click handler --> <template #footer="{closeModal}"> <button @click="closeModal"> Take me back to the app so I can be awesome </button> </template> </my-modal> </template>

Renderlose Komponenten

Und schließlich können Sie das, was Sie über die Verwendung von Slots wissen, nehmen, um wiederverwendbare Funktionen herumzureichen und praktisch den gesamten HTML-Code entfernen und einfach die Slots verwenden. Das ist im Wesentlichen, was eine Renderless-Komponente ist: eine Komponente, die nur Funktionalität ohne HTML bereitstellt.

Es kann ein wenig schwierig sein, Komponenten wirklich renderlos zu machen, da Sie render schreiben müssen, anstatt eine Vorlage zu verwenden, um die Notwendigkeit eines Root-Elements zu beseitigen, aber es ist möglicherweise nicht immer notwendig. Schauen wir uns ein einfaches Beispiel an, bei dem wir jedoch zuerst eine Vorlage verwenden können:

 <template> <transition name="fade" v-bind="$attrs" v-on="$listeners"> <slot></slot> </transition> </template> <style> .fade-enter-active, .fade-leave-active { transition: opacity 0.3s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>

Dies ist ein seltsames Beispiel für eine Renderless-Komponente, da sie nicht einmal JavaScript enthält. Das liegt hauptsächlich daran, dass wir nur eine vorkonfigurierte, wiederverwendbare Version einer integrierten Renderless-Funktion erstellen: transition .

Ja, Vue hat eingebaute Renderless-Komponenten. Dieses spezielle Beispiel stammt aus einem Artikel über wiederverwendbare Übergänge von Cristi Jora und zeigt eine einfache Möglichkeit, eine Renderless-Komponente zu erstellen, die die in Ihrer gesamten Anwendung verwendeten Übergänge standardisieren kann. Cristis Artikel geht viel tiefer und zeigt einige fortgeschrittenere Variationen von wiederverwendbaren Übergängen, daher empfehle ich, es sich anzusehen.

Für unser anderes Beispiel erstellen wir eine Komponente, die das Umschalten der Anzeige während der verschiedenen Zustände eines Promise handhabt: ausstehend, erfolgreich gelöst und fehlgeschlagen. Es ist ein gängiges Muster, und obwohl es nicht viel Code erfordert, kann es viele Ihrer Komponenten durcheinander bringen, wenn die Logik nicht für die Wiederverwendbarkeit herausgezogen wird.

 <!-- promised.vue --> <template> <span> <slot name="rejected" v-if="error" :error="error"></slot> <slot name="resolved" v-else-if="resolved" :data="data"></slot> <slot name="pending" v-else></slot> </span> </template> <script> export default { props: { promise: Promise }, data: () => ({ resolved: false, data: null, error: null }), watch: { promise: { handler (promise) { this.resolved = false this.error = null if (!promise) { this.data = null return } promise.then(data => { this.data = data this.resolved = true }) .catch(err => { this.error = err this.resolved = true }) }, immediate: true } } } </script>

Also, was ist hier los? Beachten Sie zunächst, dass wir eine Requisite namens promise Promise . Im watch wir Änderungen am Promise und wenn es sich ändert (oder sofort bei der Komponentenerstellung dank der immediate -Eigenschaft), löschen wir den Zustand und rufen then auf und catch das Promise ein, wobei wir den Zustand aktualisieren, wenn es entweder erfolgreich abgeschlossen wurde oder scheitert.

Dann zeigen wir in der Vorlage je nach Status einen anderen Slot an. Beachten Sie, dass wir es versäumt haben, es wirklich renderlos zu halten, weil wir ein Root-Element brauchten, um eine Vorlage zu verwenden. Wir leiten data und error auch an die relevanten Slot-Bereiche weiter.

Und hier ist ein Beispiel dafür, wie es verwendet wird:

 <template> <div> <promised :promise="somePromise"> <template #resolved="{ data }"> Resolved: {{ data }} </template> <template #rejected="{ error }"> Rejected: {{ error }} </template> <template #pending> Working on it... </template> </promised> </div> </template> ...

Wir übergeben somePromise an die Renderless-Komponente. Während wir darauf warten, dass es fertig ist, zeigen wir dank des pending Slots „Wird daran…“ an. Wenn es erfolgreich ist, zeigen wir „Aufgelöst:“ und den Auflösungswert an. Wenn dies fehlschlägt, zeigen wir „Abgelehnt:“ und den Fehler an, der die Ablehnung verursacht hat. Jetzt müssen wir den Status des Promise innerhalb dieser Komponente nicht mehr nachverfolgen, da dieser Teil in seine eigene wiederverwendbare Komponente gezogen wird.

Was können wir also gegen diese span tun, die sich um die Slots in promised.vue wickelt? Um es zu entfernen, müssen wir den template entfernen und unserer Komponente eine render hinzufügen:

 render () { if (this.error) { return this.$scopedSlots['rejected']({error: this.error}) } if (this.resolved) { return this.$scopedSlots['resolved']({data: this.data}) } return this.$scopedSlots['pending']() }

Hier ist nichts allzu kniffliges los. Wir verwenden nur einige if -Blöcke, um den Status zu finden, und geben dann den korrekten Slot mit Bereich zurück (über this.$scopedSlots['SLOTNAME'](...) ) und übergeben die relevanten Daten an den Bereich des Slots. Wenn Sie keine Vorlage verwenden, können Sie die Verwendung der .vue -Dateierweiterung überspringen, indem Sie das JavaScript aus dem script -Tag ziehen und es einfach in eine .js -Datei einfügen. Dies sollte Ihnen beim Kompilieren dieser Vue-Dateien einen sehr leichten Leistungsschub geben.

Dieses Beispiel ist eine abgespeckte und leicht optimierte Version von vue-promised, die ich empfehlen würde, das obige Beispiel nicht zu verwenden, da sie einige potenzielle Fallstricke abdecken. Es gibt auch viele andere großartige Beispiele für Renderless-Komponenten. Baleada ist eine ganze Bibliothek voller Renderless-Komponenten, die nützliche Funktionen wie diese bieten. Es gibt auch vue-virtual-scroller zum Steuern der Wiedergabe von Listenelementen basierend auf dem, was auf dem Bildschirm sichtbar ist, oder PortalVue zum „Teleportieren“ von Inhalten in völlig andere Teile des DOM.

Ich bin raus

Die Slots von Vue bringen die komponentenbasierte Entwicklung auf eine ganz neue Ebene, und obwohl ich viele großartige Möglichkeiten gezeigt habe, wie Slots verwendet werden können, gibt es unzählige weitere da draußen. Welche tolle Idee fällt Ihnen ein? Auf welche Weise könnten Spielautomaten Ihrer Meinung nach ein Upgrade erhalten? Wenn Sie welche haben, stellen Sie sicher, dass Sie Ihre Ideen an das Vue-Team weitergeben. Gott segne und frohes Codieren.