Hallo
Habrovchanin! Designer sind ideologische Menschen und Kunden, insbesondere mit ihren Geschäftsanforderungen.
Stellen Sie sich vor, Sie haben Ihr bestes UIkit der Welt zum coolsten Zeitpunkt zusammengestellt.% Fügen Sie Ihr% JS-Framework ein. Es scheint, dass es alles gibt, was das Projekt braucht. Jetzt können Sie Kaffee trinken und alle neuen Aufgaben schließen, indem Sie Komponenten auf die Seite werfen. Noch besser, wenn Sie ein solches UIkit
in einer Müllkippe auf NPM-Freiflächen gefunden haben und es perfekt zur aktuellen UX / UI und ihren Anforderungen passt. Fiktion!
Und wirklich ... wen scherze ich? Ihr Glück ist wahrscheinlich nur von kurzer Dauer. Wenn der Designer mit dem Talmud neuer UI-Lösungen für die nächste Seite oder das „spezielle Projekt“ zum Laufen kommt, wird ohnehin etwas schief gehen.
An dieser Stelle steht der Entwickler vor der Frage
"TROCKEN oder nicht TROCKEN" ? Sollte ich vorhandene Komponenten irgendwie anpassen? Ja, um die Regression bestehender Fälle nicht zu verzögern. Oder handeln Sie nach dem Prinzip „funktioniert - nicht anfassen“ und schreiben Sie neue Komponenten von Grund auf neu. Gleichzeitig wird das UIkit aufgeblasen und die Unterstützung erschwert.
Wenn Sie, wie viele, in einer solchen Situation waren, schauen Sie unter den Schnitt!
Trotz der ausführlichen Einführung kam mir die Idee, diesen Artikel zu schreiben, nachdem ich
einen der Kommentarthreads zu Habré gelesen hatte. Dort wurden die Jungs ernsthaft darüber besprüht, wie man die Tastenkomponente auf React anpasst. Nun, nachdem ich ein paar solcher Holivars in Telegram gesehen hatte, wurde die Notwendigkeit, darüber zu schreiben, endlich verstärkt.
Stellen wir uns zunächst vor, welche „Anpassungen“ wir möglicherweise auf die Komponente anwenden müssen.
Stile
Dies ist zunächst die Anpassung von Komponentenstilen. Ein alltägliches Beispiel ist ein grauer Knopf, aber ein blauer wird benötigt. Oder ein Knopf ohne abgerundete Ecken und plötzlich werden sie benötigt. Aufgrund der von mir gelesenen Holivars kam ich zu dem Schluss, dass es hierfür ungefähr drei Ansätze gibt:
1. Globale Stile
Nutzen Sie die
volle Leistung globaler CSS-Stile, die von
! Wichtig umgeleitet werden
. Wichtig , damit Sie außerhalb der Welt versuchen, die Stile der ursprünglichen Komponente zu überlappen. Die Entscheidung ist, gelinde gesagt, umstritten und zu einfach. Darüber hinaus ist eine solche Option einfach nicht immer möglich und verstößt gleichzeitig verzweifelt gegen die Kapselung von Stilen. Es sei denn natürlich, es wird in Ihren Komponenten verwendet.
2. Übergeben von Klassen (Stilen) aus dem übergeordneten Kontext
Auch eine ziemlich kontroverse Entscheidung. Es stellt sich heraus, dass wir eine spezielle
Requisite erstellen,
sozusagen zum Beispiel als
Klassen, und direkt darüber tauchen wir die erforderlichen Klassen in die Komponente ein.
<Button classes="btn-red btn-rounded" />
Ein solcher Ansatz funktioniert natürlich nur, wenn die Komponente das Anwenden von Stilen auf ihren Inhalt auf diese Weise unterstützt. Wenn die Komponente etwas komplexer ist und aus einer verschachtelten Struktur von HTML-Elementen besteht, ist es natürlich problematisch, Stile auf alle anzuwenden. Sie werden also auf das Stammelement der Komponente angewendet und dann mithilfe von CSS-Regeln irgendwie weiter verbreitet. Leider.
3. Einstellen der Komponente mit Requisiten
Es sieht aus wie die vernünftigste, aber gleichzeitig die am wenigsten flexible Lösung. Einfach ausgedrückt, wir erwarten, dass der Autor der Komponente eine Art Genie ist und alle Optionen im Voraus durchdacht hat. Das heißt, alles, was wir brauchen und alle notwendigen Requisiten für alle gewünschten Ergebnisse bestimmen:
<Button bgColor="red" rounded={true} />
Das klingt nicht sehr wahrscheinlich, oder? Vielleicht.
Verhalten

Hier ist es noch mehrdeutiger. Erstens, weil die Schwierigkeiten beim Anpassen des Verhaltens einer Komponente von der Aufgabe herrühren. Je komplexer die Komponente und die Logik darin sind und je komplexer die Änderung ist, die wir vornehmen möchten, desto schwieriger ist es, diese Änderung vorzunehmen. Es stellte sich eine Art Tautologie heraus ... Kurz gesagt, Sie verstehen! ;-);
Selbst hier gibt es jedoch eine Reihe von Tools, mit denen wir die Komponente entweder anpassen können oder nicht. Da wir speziell über den Komponentenansatz sprechen, möchte ich die folgenden nützlichen Werkzeuge hervorheben:
1. Bequeme Arbeit mit Requisiten
Zunächst müssen Sie in der Lage sein, ein Komponenten-Requisitenset nachzuahmen, ohne dieses Set neu beschreiben und bequem weiter vertreten zu müssen.
Wenn wir versuchen, der Komponente ein Verhalten hinzuzufügen, müssen wir höchstwahrscheinlich einen zusätzlichen Satz von Requisiten verwenden, die von der ursprünglichen Komponente nicht benötigt werden. Daher ist es gut, einen Teil der Requisiten abschneiden und nur das Notwendige auf die Originalkomponente übertragen zu können. Gleichzeitig bleiben alle Eigenschaften synchron.
Die Kehrseite ist, wenn wir einen speziellen Fall des Komponentenverhaltens implementieren möchten. Irgendwie einen Teil seines Zustands auf eine bestimmte Aufgabe zu fixieren.
2. Verfolgung des Lebenszyklus und der Ereignisse von Komponenten
Mit anderen Worten, alles, was innerhalb einer Komponente geschieht, sollte kein vollständig geschlossenes Buch sein. Ansonsten erschwert es die Anpassung seines Verhaltens erheblich.
Ich meine nicht die Verletzung der Kapselung und unkontrollierte Störungen im Inneren. Die Komponente sollte über ihre öffentliche API verwaltet werden (normalerweise handelt es sich um Requisiten und / oder Methoden). Aber um irgendwie „herausfinden“ zu können, was im Inneren passiert, und die Veränderung seines Zustands verfolgen zu können, ist es immer noch notwendig.
3. Imperatives Management
Wir gehen davon aus, dass ich Ihnen das nicht gesagt habe. Und doch ist es manchmal schön, eine Instanz einer Komponente zu erhalten und unbedingt „die Fäden zu ziehen“. Es ist besser, dies zu vermeiden, aber in besonders komplexen Fällen können Sie nicht darauf verzichten.
Ok, irgendwie die Theorie geklärt. Im Großen und Ganzen ist alles offensichtlich, aber nicht alles ist klar. Daher lohnt es sich, zumindest einen realen Fall zu betrachten.
Fall
Ich habe oben erwähnt, dass die Idee, einen Artikel zu schreiben, aufgrund von Holivar über das Anpassen einer Schaltfläche entstanden ist. Daher dachte ich, es wäre symbolisch, einen solchen Fall zu lösen. Es wäre zu einfach, die Farbe zu ändern oder Ecken abzurunden, deshalb habe ich versucht, einen etwas komplexeren Fall zu finden.
Stellen Sie sich vor, wir haben eine bestimmte Komponente der Basistaste, die beim Gefängnis von App-Standorten verwendet wird. Darüber hinaus werden einige grundlegende Verhaltensweisen für alle Anwendungsschaltflächen sowie eine Reihe grundlegender, gekapselter Stile implementiert, die von Zeit zu Zeit mit Benutzeroberflächenhandbüchern und all dem synchronisiert werden.
Außerdem muss eine zusätzliche Komponente für die Schaltfläche "Senden" an den Server vorhanden sein (Schaltfläche "Senden"), die zusätzlich zu Stiländerungen zusätzliches Verhalten erfordert. Dies kann beispielsweise eine Zeichnung des Sendefortschritts sowie eine visuelle Darstellung des Ergebnisses dieser Aktion sein - erfolgreich oder nicht erfolgreich.
Es könnte ungefähr so aussehen:
Es ist nicht schwer zu erraten, dass sich die Basisschaltfläche links befindet und die Senden-Schaltfläche rechts sich in einem Zustand befindet, in dem die Anforderung erfolgreich abgeschlossen wurde. Nun, wenn der Fall klar ist - fangen wir an!
Lösung
Ich konnte immer noch nicht herausfinden, was genau den Holivar in der Entscheidung über React verursachte. Anscheinend ist es nicht so einfach. Daher werde ich mein Glück nicht versuchen und das bekanntere Tool -
SvelteJS - verwenden,
ein Framework zum Verschwinden der neuen Generation , das fast perfekt zur Lösung
solcher Probleme geeignet ist.
Wir sind uns sofort einig, dass wir den Code der Basistaste in keiner Weise stören. Wir gehen davon aus, dass es überhaupt nicht von uns geschrieben wurde und sein Code wegen Korrekturen geschlossen ist. In diesem Fall sieht die Komponente der Basistaste ungefähr so aus:
Button.html <button {type} {name} {value} {disabled} {autofocus} on:click > <slot></slot> </button> <script> export default { data() { return { type: 'button', disabled: false, autofocus: false, value: '', name: '' }; } }; </script> <style> </style>
Und so verwendet:
<Button on:click="cancel()">Cancel</Button>
Bitte beachten Sie, dass die Tastenkomponente wirklich sehr einfach ist. Es enthält absolut keine Hilfselemente oder Requisiten, die bei der Implementierung der erweiterten Version der Komponente hilfreich sein könnten. Diese Komponente unterstützt nicht einmal die Übertragung von Stilen durch Requisiten oder zumindest eine Art eingebaute Anpassung, und alle Stile sind streng isoliert und treten nicht aus.
Auf der Grundlage dieser Komponente eine weitere mit erweiterter Funktionalität und auch ohne Änderungen zu erstellen, scheint eine einfache Aufgabe zu sein. Aber nicht, wenn Sie
Svelte verwenden .
Lassen Sie uns nun bestimmen, was die Schaltfläche "Senden" tun soll:
- Zunächst sollten der Rahmen- und Schaltflächentext grün sein. Beim Schweben sollte der Hintergrund auch grün statt dunkelgrau sein.
- Wenn eine Taste gedrückt wird, sollte sie in eine runde Fortschrittsanzeige „knallen“.
- Nach Abschluss des Prozesses (der extern gesteuert wird) muss der Status der Schaltfläche in erfolgreich (Erfolg) oder nicht erfolgreich (Fehler) geändert werden. Gleichzeitig sollte sich der Knopf von der Anzeige entweder in ein grünes Abzeichen mit einer Morgendämmerung oder in ein rotes Abzeichen mit einem Kreuz verwandeln.
- Es ist auch erforderlich, die Zeit einstellen zu können, nach der sich das entsprechende Abzeichen wieder in einen Knopf im ursprünglichen Zustand (Leerlauf) verwandelt.
- Und natürlich müssen Sie dies alles über der Basistaste tun, indem Sie alle Stile und Requisiten von dort aus speichern und anwenden.
Fuh, keine leichte Aufgabe. Lassen Sie uns zuerst eine neue Komponente erstellen und sie mit der Basistaste umschließen:
SubmitButton.html <Button> <slot></slot> </Button> <script> import Button from './Button.html'; export default { components: { Button } }; </script>
Dies ist zwar genau die gleiche Schaltfläche, aber nur schlimmer - sie weiß nicht einmal, wie man Requisiten als Proxy verwendet. Dies spielt keine Rolle, wir werden später darauf zurückkommen.
Stilisieren
Lassen Sie uns in der Zwischenzeit darüber nachdenken, wie wir eine neue Schaltfläche gestalten können, nämlich die Farben je nach Aufgabe ändern. Leider scheint es, dass wir keinen der oben beschriebenen Ansätze verwenden können.
Da Stile innerhalb einer Schaltfläche isoliert sind, können Probleme mit globalen Stilen auftreten. Es ist auch unmöglich, Stile zu finden - die Basistaste unterstützt diese Funktion einfach nicht. Sowie das Erscheinungsbild mit Hilfe von Requisiten anpassen. Darüber hinaus möchten wir, dass alle für die neue Schaltfläche geschriebenen Stile auch in diese Schaltfläche eingekapselt werden und nicht auslaufen.
Die Lösung ist unglaublich einfach, aber nur, wenn Sie
Svelte bereits verwenden. Schreiben Sie einfach die Stile für die neue Schaltfläche:
<div class="submit"> <Button> <slot></slot> </Button> </div> ... <style> .submit :global(button) { border: 2px solid #1ECD97; color: #1ECD97; } .submit :global(button:hover) { background-color: #1ECD97; color: #fff; } </style>
Eine der Keynotes von
Svelte - einfache Dinge sollten einfach gelöst werden. Der spezielle Modifikator
: global in dieser Version generiert CSS so, dass nur die Schaltflächen innerhalb des Blocks mit der
Submit- Klasse, die sich in dieser Komponente befinden, die angegebenen Stile erhalten.
Auch wenn das gleiche Markup plötzlich an einer anderen Stelle in der Anwendung erscheint:
<div class="submit"> <button>Button</button> </div>
Stile aus der
SubmitButton- Komponente "lecken" dort
in keiner Weise.
Mit dieser Methode erleichtert
Svelte das einfache Anpassen der Stile verschachtelter Komponenten, während die Kapselung der Stile beider Komponenten beibehalten wird.
Wir werfen Requisiten und korrigieren das Verhalten
Nun, wir haben uns fast sofort und ohne zusätzliche Requisiten mit dem Styling befasst und CSS-Klassen direkt bestanden. Jetzt müssen Sie alle Requisiten der
Button- Komponente durch die neue Komponente vertreten. Ich würde sie jedoch nicht noch einmal beschreiben wollen. Lassen Sie uns jedoch zunächst entscheiden, welche Eigenschaften die neue Komponente haben wird.
Nach der Aufgabe zu
urteilen, sollte
SubmitButton den Status überwachen und die Möglichkeit bieten, die Zeitverzögerung zwischen der automatischen Änderung eines erfolgreichen / fehlerhaften Status in den ursprünglichen Status anzugeben:
<script> ... export default { ... data() { return { delay: 1500, status: 'idle' </script>
Unsere neue Schaltfläche hat also 4 Status: Ruhe, Download, Erfolg oder Fehler. Außerdem wechseln die letzten beiden Zustände standardmäßig nach 1,5 Sekunden automatisch in den Ruhezustand.
Um alle übertragenen Requisiten in die
Button- Komponente zu werfen, aber gleichzeitig den
Status und die
Verzögerung abzuschneiden
, die offensichtlich ungültig sind, schreiben wir eine speziell berechnete Eigenschaft. Danach verwenden wir einfach den
Spread- Operator, um die verbleibenden Requisiten auf der eingebetteten Komponente zu „verschmieren“. Da wir genau die Schaltfläche "Senden" ausführen, müssen wir außerdem den Typ der Schaltfläche so festlegen, dass er von außen nicht geändert werden kann:
<div class="submit"> <Button {...attrs} type="submit"> <slot></slot> </Button> </div> <script> ... export default { ... computed: { attrs: data => { const { delay, status, ...attrs } = data; return attrs; } }, }; </script>
Ziemlich einfach und elegant.
Als Ergebnis haben wir eine voll funktionsfähige Version der Basisschaltfläche mit geänderten Stilen erhalten. Es ist Zeit, das neue Tastenverhalten zu implementieren.
Wir ändern und verfolgen den Status
Wenn Sie also auf die Schaltfläche
SubmitButton klicken
, müssen wir das Ereignis nicht nur
auslösen , damit der Benutzercode es verarbeiten kann (wie in
Button ausgeführt ), sondern auch zusätzliche Geschäftslogik implementieren - den Download-Status festlegen. Nehmen Sie dazu einfach das Ereignis von der Basistaste in Ihren eigenen Handler, tun Sie, was Sie brauchen, und senden Sie es weiter:
<div class="submit"> <Button {...attrs} type="submit" on:click="click(event)"> <slot></slot> </Button> </div> <script> ... export default { ... methods: { click(e) { this.set({ status: 'loading' }); this.fire('click', e); } }, }; </script>
Darüber hinaus kann die übergeordnete Komponente dieser Schaltfläche, die den Prozess des Sendens von Daten selbst steuert, den entsprechenden Sendestatus (
Erfolg / Fehler ) über Requisiten festlegen. Gleichzeitig sollte die Schaltfläche eine solche Statusänderung verfolgen und nach einer bestimmten Zeit automatisch den Status in
Leerlauf ändern. Verwenden Sie dazu den
Aktualisierungs- Hook für den
Lebenszyklus :
<script> ... export default { ... onupdate({ current: { status, delay }, changed }) { if (changed.status && ['success', 'error'].includes(status)) { setTimeout(() => this.set({ status: 'idle' }), delay); } }, }; </script>
Feinschliff
Es gibt zwei weitere Punkte, die aus der Aufgabe nicht ersichtlich sind und sich während der Implementierung ergeben. Erstens müssen Sie die Schaltfläche selbst mit den Stilen und nicht mit einem anderen Element ändern, damit die Animation von Schaltflächenmetamorphosen reibungslos verläuft. Zu diesem Zweck können wir dasselbe verwenden
: global , sodass es keine Probleme gibt. Darüber hinaus muss das Markup in der Schaltfläche in allen Status außer im
Leerlauf ausgeblendet sein.
Es ist erwähnenswert, dass das Markup innerhalb der Schaltfläche beliebig sein kann und durch verschachtelte Slots in die ursprüngliche Komponente der Basistaste geworfen wird. Obwohl es bedrohlich klingt, ist die Lösung mehr als primitiv - Sie müssen nur den Steckplatz in der neuen Komponente in ein zusätzliches Element einwickeln und die erforderlichen Stile darauf anwenden:
<div class="submit"> <Button {...attrs} type="submit" on:click="click(event)"> <span><slot></slot></span> </Button> </div> ... <style> ... .submit span { transition: opacity 0.3s 0.1s; } .submit.loading span, .submit.success span, .submit.error span { opacity: 0; } ... </style>
Da sich die Schaltfläche nicht vor der Seite verbirgt, sondern sich zusammen mit den Status ändert, ist es außerdem hilfreich, sie zum Zeitpunkt des Sendens zu deaktivieren. Mit anderen Worten, wenn die Senden-Schaltfläche mithilfe von Requisiten
deaktiviert wurde oder wenn der Status nicht
inaktiv ist , müssen Sie die Schaltfläche deaktivieren. Um dieses Problem zu lösen, schreiben wir eine weitere kleine berechnete Eigenschaft
isDisabled und wenden sie auf die verschachtelte Komponente an:
<div class="submit"> <Button {...attrs} type="submit" disabled={isDisabled}> <span><slot></slot></span> </Button> </div> <script> ... export default { ... computed: { ... isDisabled: ({ status, disabled }) => disabled || status !== 'idle' }, }; </script>
Alles wäre in Ordnung, aber die grundlegende Schaltfläche hat einen Stil, der sie im deaktivierten Zustand durchscheinend macht. Wir benötigen sie jedoch nicht, wenn die Schaltfläche aufgrund einer Statusänderung nur vorübergehend deaktiviert ist. Trotzdem kommt zur Rettung
: global :
.submit.loading :global(button[disabled]), .submit.success :global(button[disabled]), .submit.error :global(button[disabled]) { opacity: 1; }
Das ist alles! Der neue Knopf ist wunderschön und bereit zu gehen!
Ich werde absichtlich Details der Implementierung von Animationen und all dies weglassen. Nicht nur, weil es nicht direkt mit dem Thema des Artikels zusammenhängt, sondern auch, weil die Demo in diesem Teil nicht so ausfiel, wie wir es gerne hätten. Ich habe meine Aufgabe nicht kompliziert und eine komplett schlüsselfertige Lösung für eine solche Schaltfläche implementiert und ein im Internet gefundenes Beispiel ziemlich dumm portiert.
Daher rate ich nicht, diese Implementierung in der Arbeit zu verwenden. Denken Sie daran, dies ist nur eine Demo für diesen Artikel.
→
Interaktive Demo und vollständiger BeispielcodeWenn Ihnen der Artikel gefallen hat und Sie mehr über
Svelte erfahren
möchten ,
lesen Sie andere
Artikel . Beispiel:
"So führen Sie eine Benutzersuche auf GitHub ohne React + RxJS 6 + Recompose durch .
" Hören Sie sich den Neujahrspodcast
RadioJS # 54 an , in dem ich ausführlich darüber sprach, was
Svelte ist , wie es „verschwindet“ und warum es nicht „noch ein js-Framework“ ist.
Schauen Sie sich den russischsprachigen Telegrammkanal
SvelteJS an . Wir sind bereits mehr als zweihundert und wir werden uns freuen, Sie zu sehen!
P / sPlötzlich änderten sich die UI-Richtlinien. Jetzt sollten die Beschriftungen in allen Schaltflächen der Anwendung in Großbuchstaben geschrieben sein. Wir haben jedoch keine Angst vor einer solchen Wende. Texttransformation
hinzufügen: Großbuchstaben; in den Stilen des Basisknopfes und weiterhin Kaffee trinken.
Einen schönen Arbeitstag noch!