In meinem Kopf lautet der Headspace für MVC -Design: „Wie modelliere ich meine Geschäftsdomäne? Wie modelliere ich die Prozesse der Interaktion mit dieser Domäne? Wie modelliere ich die Schnittstelle, um diese Prozesse zu erleichtern? “ Ich bin der Meinung, dass dieser Headspace kein gutes Komponentendesign erleichtert. Tatsächlich ist es das polare Gegenteil dafür, wie Sie denken sollten, wenn Sie eine Schnittstelle in komponierbare Komponenten zerlegen. Bestenfalls erhalten Sie Micro -Apps. Im schlimmsten Fall bauen Sie Gottkomponenten auf. Das Letzte, was Sie tun möchten, ist, Ihre Geschäftsdomäne als Komponenten zu modellieren. Was Sie anstreben sollten, um zu modellieren, sind die kleinsten abstrakten Interaktionsabschnitte, die Sie beschreiben können.
Entwurf für Wiederverwendung
Machen Sie als ein weiteres Beispiel keine Typ-Ahead-Hilfe-Suchkomponente, die überall verwendet werden, wo Sie das Durchsuchen des Help-Systems zulassen möchten, und erstellen Sie eine suggestive Texteingangskomponente, die über die Interaktionen zur Bereitstellung von Eingabebedingungen informiert ist. Nehmen Sie anschließend eine Hilfesuche -API -Datenkomponente durch, die weiß, wie Anforderungen für Daten empfangen werden, und interagieren Sie mit der Help -Suche API und Broadcast -Ergebnisse. Jetzt benötigen die Tests Ihres Suggestivtexteingangs keine APIs verspottet. Wenn Sie aufgefordert werden, einem Feld „Tag“ Vorschläge hinzuzufügen, können Sie Ihre vorhandene Suggestiv -Texteingabetomponente fallen lassen, eine einfache Datenkomponente, die Gespräche spricht zur Tag -API und fertig!
Für ein konkretes Beispiel sehen wir uns die Implementierung einer einfachen Schnittstelle als isolierte Komponenten an. Das folgende Modell ist eine Extraktion aus dem 1-zu-1-Projektsystem von 99Designs. Während die Benutzeroberfläche drastisch vereinfacht wurde, ist das JavaScript, dem wir aufbauen werden, der Produktionscode von unserer Website zum Zeitpunkt des Schreibens. Hier ist der Wireframe:
Was wir haben, ist die Navigation zwischen drei Projektlisten - aktiv, Entwürfe und archiviert. Jedes Projekt hat eine Aktion, die darauf aus ausgeführt werden kann-archivieren ein aktives Projekt, das Löschen eines Entwurfs oder die Wiedereinaktivierung eines archivierten Projekts. Im App -Design -Denken würden wir mit der Modellierung eines Projekts beginnen und ihm Methoden wie „Archiv“ und „Löschen“ sowie eine „Status“ -Fahrung geben, um zu verfolgen, in welcher der drei Listen es gehört. Genau das, was wir vermeiden wollen, werden wir uns nur mit den Interaktionen und dem, was nötig ist, um sie zu erleichtern, uns kümmern.
Im Kern haben wir eine Aktion pro Zeile. Wenn diese Aktion ausgeführt wird, möchten wir die Zeile aus der Liste entfernen. Wir haben bereits ein projektspezifisches Domänenwissen abgelegt! Darüber hinaus zählen wir mit der Anzahl der viele Elemente in jeder Liste. Um den Umfang dieses Artikels zurückzuhalten, gehen wir davon aus, dass jede serverseitige Seite mit der Registerkartennavigation eine vollständige Aktualisierung der Seite verursacht. Da wir nicht die Abhängigkeit von JavaScript erzwingen müssen, werden unsere Aktionsschaltflächen Elemente mit Submit -Ereignishandlern sein, die asynchron die Aktion des Formulars ausführen und ein Ereignis übertragen, wenn es abgeschlossen ist.
Hier ist ein HTML für eine einzelne Projektzeile:
<span><span><span><li</span>></span> </span> <span><span><span><a</span> href<span>="/projects/99"</span> title<span>="View project"</span>></span>Need sticker designs for XYZ Co.<span><span></a</span>></span> </span> <span><span><span><div</span> class<span>="project__actions"</span>></span> </span> <span><span><span><a</span> href<span>="/projects/99"</span> class<span>="button"</span>></span>View<span><span></a</span>></span> </span> <span><span><span><form</span> class<span>="action"</span> action<span>="/projects/99/archive"</span> method<span>="post"</span>></span> </span> <span><span><span><button</span>></span>Archive<span><span></button</span>></span> </span> <span><span><span></form</span>></span> </span> <span><span><span></div</span>></span> </span><span><span><span></li</span>></span></span>
Ich werde den Flug verwenden, um unsere Komponenten zu erstellen. Der Flug ist derzeit unsere Standard -JS -Komponentenbibliothek bei 99Designs aus den Gründen, die ich in meinem vorherigen SitePoint -JavaScript -Artikel beschrieben habe.
Hier ist unsere asyncForm -Komponente zum Abwickeln der Formulareingabe und der Übertragung eines Ereignisses:
<span>define(function(require) { </span> <span>'use strict'; </span> <span>var defineComponent = require('flight/lib/component'); </span> <span>function <span>AsyncForm</span>() { </span> <span>this.defaultAttrs({ </span> <span>broadcastEvent: 'uiFormProcessed' </span> <span>}); </span> <span>this.after('initialize', function() { </span> <span>this.on(this.node, 'submit', this.asyncSubmit.bind(this)); </span> <span>}); </span> <span>this.asyncSubmit = function(event) { </span> event<span>.preventDefault(); </span> $<span>.ajax({ </span> <span>'url': this.$node.attr('action'), </span> <span>'dataType': 'json', </span> <span>'data': this.$node.serializeArray(), </span> <span>'type': this.$node.attr('method') </span> <span>}).done(function(response<span>, data</span>) { </span> <span>this.$node.trigger(this.attr.broadcastEvent, data); </span> <span>}.bind(this)).fail(function() { </span> <span>// error handling excluded for brevity </span> <span>}); </span> <span>}; </span> <span>} </span> <span>return defineComponent(AsyncForm); </span><span>});</span>
Wir behalten eine strenge Richtlinie für die Verwendung von Klassenattributen für JavaScript bei. Daher fügen wir unseren Aktionsformularen ein Data-async-Formattribut hinzu und addieren unsere Komponenten an alle übereinstimmenden Formulare wie SO:
<span><span><span><li</span>></span> </span> <span><span><span><a</span> href<span>="/projects/99"</span> title<span>="View project"</span>></span>Need sticker designs for XYZ Co.<span><span></a</span>></span> </span> <span><span><span><div</span> class<span>="project__actions"</span>></span> </span> <span><span><span><a</span> href<span>="/projects/99"</span> class<span>="button"</span>></span>View<span><span></a</span>></span> </span> <span><span><span><form</span> class<span>="action"</span> action<span>="/projects/99/archive"</span> method<span>="post"</span>></span> </span> <span><span><span><button</span>></span>Archive<span><span></button</span>></span> </span> <span><span><span></form</span>></span> </span> <span><span><span></div</span>></span> </span><span><span><span></li</span>></span></span>
Jetzt können wir die Aktion ausführen und ein Ereignis übertragen, das den Dom -Baum zum Erfolg ausbreitet. Der nächste Schritt ist es, dieses Ereignis zu hören und die Zeile zu entfernen, zu der sie sprudelt. Dafür haben wir abnehmbar:
<span>define(function(require) { </span> <span>'use strict'; </span> <span>var defineComponent = require('flight/lib/component'); </span> <span>function <span>AsyncForm</span>() { </span> <span>this.defaultAttrs({ </span> <span>broadcastEvent: 'uiFormProcessed' </span> <span>}); </span> <span>this.after('initialize', function() { </span> <span>this.on(this.node, 'submit', this.asyncSubmit.bind(this)); </span> <span>}); </span> <span>this.asyncSubmit = function(event) { </span> event<span>.preventDefault(); </span> $<span>.ajax({ </span> <span>'url': this.$node.attr('action'), </span> <span>'dataType': 'json', </span> <span>'data': this.$node.serializeArray(), </span> <span>'type': this.$node.attr('method') </span> <span>}).done(function(response<span>, data</span>) { </span> <span>this.$node.trigger(this.attr.broadcastEvent, data); </span> <span>}.bind(this)).fail(function() { </span> <span>// error handling excluded for brevity </span> <span>}); </span> <span>}; </span> <span>} </span> <span>return defineComponent(AsyncForm); </span><span>});</span>
Auch hier fügen wir unseren Projektzeilen ein datenauslösbares Attribut hinzu und fügen die Komponente den Zeilenelementen hinzu:
<span>AsyncForm.attachTo('[data-async-form]');</span>
fertig! Zwei kleine Komponenten mit jeweils einem Ereignis, und wir haben die drei Arten von Aktionen in unseren drei Formen auf eine Weise behandelt, die sich anmutig verschlechtert. Nur noch eine Sache, und das ist unsere Zählung auf jeder Registerkarte. Sollte einfach genug sein, wir brauchen alles, was wir brauchen, um die Anzahl der aktiven Registerkarten jedes Mal um eins um eins zu verringern, wenn eine Reihe entfernt wird. Aber warte! Wenn ein aktives Projekt archiviert wird, muss die archivierte Anzahl zunehmen, und wenn ein archiviertes Projekt wieder aktiviert wird, muss die aktivierte Anzahl zunehmen. Lassen Sie uns zunächst eine Count -Komponente erstellen, die Anweisungen erhalten kann, um seine Nummer zu ändern:
<span>define(function(require) { </span> <span>'use strict'; </span> <span>var defineComponent = require('flight/lib/component'); </span> <span>function <span>Removable</span>() { </span> <span>this.defaultAttrs({ </span> <span>'removeOn': 'uiFormProcessed' </span> <span>}); </span> <span>this.after('initialize', function() { </span> <span>this.on(this.attr.removeOn, this.remove.bind(this)); </span> <span>}); </span> <span>this.remove = function(event) { </span> <span>// Animate row removal, remove DOM node, teardown component </span> $<span>.when(this.$node </span> <span>.animate({'opacity': 0}, 'fast') </span> <span>.slideUp('fast') </span> <span>).done(function() { </span> <span>this.$node.remove(); </span> <span>}.bind(this)); </span> <span>}; </span> <span>} </span> <span>return defineComponent(Removable); </span><span>});</span>
Unsere Zählung würde in HTML als etwas wie
<span>Removable.attachTo('[data-removable]');</span>
Das letzte Stück des Puzzles erhält unsere abnehmbaren Instanzen, um ein Ereignis mit einem Modifikator zu ihren jeweiligen Zählen zu entlassen, wenn sie entfernt werden. Wir möchten sicherlich keine Kopplung zwischen den Komponenten, daher geben wir ein abnehmbares Attribut, das eine Reihe von Ereignissen enthält, wenn es entfernt wird:
<span>define(function(require) { </span> <span>'use strict'; </span> <span>var defineComponent = require('flight/lib/component'); </span> <span>function <span>Count</span>() { </span> <span>this.defaultAttrs({ </span> <span>'event': null </span> <span>}); </span> <span>this.after('initialize', function() { </span> <span>this.on(document, this.attr.event, this.update.bind(this)); </span> <span>}); </span> <span>this.update = function(event<span>, data</span>) { </span> <span>this.$node.text( </span> <span>parseInt(this.$node.text(), 10) + data.modifier </span> <span>); </span> <span>} </span> <span>} </span> <span>return defineComponent(Count); </span><span>});</span>
Jetzt tritt die Kopplung zwischen Zähl und Wechsel im verwendungs casespezifischen Seitenskript auf, in dem wir unsere Komponenten an das DOM anhängen:
<span>Count.attachTo( </span> <span>'[data-counter="active"]', </span> <span>{'event': 'uiActiveCountChanged'} </span><span>); </span> <span>Count.attachTo( </span> <span>'[data-counter="draft"]', </span> <span>{'event': 'uiDraftCountChanged'} </span><span>); </span> <span>Count.attachTo( </span> <span>'[data-counter="archived"]', </span> <span>{'event': 'uiArchivedCountChanged'} </span><span>);</span>
Mission erfüllt. Unsere Zähler wissen nichts über unsere Projektlistenreihen, die nichts über die Formen in ihnen wissen. Und keiner der Komponenten ist im geringsten auf das Konzept einer Liste von Projekten entwickelt.
Unser UX -Designer hat darauf hingewiesen, dass es besser wäre, wenn wir um Bestätigung fragen, wenn jemand versucht, einen Entwurf zu löschen, da diese Aktion nicht rückgängig gemacht werden kann. Kein Problem, wir können eine Komponente aufschlagen, die genau das tut:
<span>define(function(require) { </span> <span>'use strict'; </span> <span>var defineComponent = require('flight/lib/component'); </span> <span>function <span>Removable</span>() { </span> <span>this.defaultAttrs({ </span> <span>'removeOn': 'uiFormProcessed', </span> <span>'broadcastEvents': [ </span> <span>{'event': 'uiRemoved', 'data': {}} </span> <span>] </span> <span>}); </span> <span>this.after('initialize', function() { </span> <span>this.on(this.attr.removeOn, this.remove.bind(this)); </span> <span>}); </span> <span>this.remove = function(event) { </span> <span>// Broadcast events to notify the rest of the UI that this component has been removed </span> <span>this.attr.broadcastEvents.forEach(function(eventObj) { </span> <span>this.trigger(eventObj.event, eventObj.data); </span> <span>}.bind(this)); </span> <span>// Animate row removal, remove DOM node, teardown component </span> $<span>.when(this.$node </span> <span>.animate({'opacity': 0}, 'fast') </span> <span>.slideUp('fast') </span> <span>).done(function() { </span> <span>this.$node.remove(); </span> <span>}.bind(this)); </span> <span>}; </span> <span>} </span> <span>return defineComponent(Removable); </span><span>});</span>
Befestigen Sie das an die Löschtasten, und wir haben das, wonach wir gefragt wurden. Im Dialogfeld "Bestätigung" wird die Schaltfläche abgefangen und die Einreichung des Formulars zulassen, wenn der Benutzer "OK" auswählt. Wir mussten unsere asyncForm -Komponente nicht ändern, da wir diese Komponenten komponieren können, ohne uns gegenseitig zu stören. In unserem Produktionscode verwenden wir auch eine SingleSubmit -Komponente für die Aktionstaste, die visuelles Feedback gibt, das das Formular eingereicht hat, und verhindert mehrere Einreichungen.
Hoffentlich hat dieser Artikel gezeigt, wie Ihre Projekte davon profitieren können, Schnittstellen in komponierbare Komponenten zu zerlegen. Ein wichtiger Vorteil des Komponentendesigns, das ich nicht abgedeckt habe, ist die einfache isolierte Tests. Hier sind die endgültigen Komponenten zusammen mit ihren Jasmin -Tests und HTML -Testvorstellungen:
Wenn Sie Fragen zu dem haben, was ich behandelt habe, fragen Sie bitte Details in den Kommentaren und ich werde mein Bestes tun, um zu helfen.
Wie kann ich meine Fähigkeiten zum kritischen Denken verbessern? Gewohnheiten wie die Befragung von Annahmen, die Suche nach verschiedenen Perspektiven und das offene für neue Ideen. Es beinhaltet auch die Entwicklung von Fähigkeiten wie Analyse, Interpretation, Inferenz, Bewertung, Erklärung und Selbstregulierung. kritisches Denken. Es geht dar Die Informationen oder Ideen, die wir in Betracht ziehen, stehen in direktem Zusammenhang mit dem Problem oder dem vorliegenden Problem. Es hilft uns, konzentriert zu bleiben und Ablenkungen oder irrelevante Informationen zu vermeiden.
Wie kann ich in meinem Denken Breite entwickeln?
Wie kann ich die Klarheit meines Denkens verbessern? Einfachheit und Unkomplizierung in unseren Gedanken und Ausdrücken.
Das obige ist der detaillierte Inhalt vonIn Komponenten denken. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!