Bauen Sie Ihren eigenen Yeoman Generator

Die Generatoren von Yeoman verleihen ihrer Plattform Flexibilität. Sie können dasselbe Tool für alle Arten von Projekten, an denen Sie arbeiten, JavaScript oder andere, wiederverwenden, und das ist, bevor ich die riesige Bibliothek von über 400 von der Community bereitgestellten Generatoren erwähne. Manchmal haben Sie jedoch möglicherweise ein bestimmtes Setup, das Sie gerne in Ihren eigenen Projekten verwenden möchten. In solchen Situationen ist es sinnvoll, die gesamte Speicherplatte in Ihren eigenen Generator zu abstrahieren.

In diesem Artikel werden wir einen Yeoman-Generator erstellen, der es uns ermöglicht, eine Seite für eine einzelne Seite zu erstellen. Dies ist ein ziemlich einfaches Beispiel, aber es wird uns ermöglichen, viele der interessanteren Funktionen zu behandeln, die Yeoman bereitstellt.

Erste Schritte

Ich gehe davon aus, dass Sie Node.js eingerichtet haben. Wenn nicht, können Sie die Installation von hier aus abrufen. Außerdem müssen wir Yeoman sowie den Generator für die Generierung von Generatoren installieren. Dies können Sie mit dem folgenden Befehl erreichen npm:

npm install -g yo Generator-Generator 

Schließlich müssen wir das eigentliche Projekt erstellen. Navigieren Sie zu dem Ordner Ihrer Wahl und führen Sie Folgendes aus:

Ihr Generator 

Dies startet den Generator und stellt einige Fragen wie den Projektnamen und Ihren GitHub-Account. Für diesen Artikel benenne ich meinen Generator eine Seite.

Die Dateistruktur

Seien Sie nicht beunruhigt über die große Anzahl von Dateien, die durch den Befehl erzeugt werden, es wird alles in einem Moment sinnvoll sein.

Die ersten paar Dateien sind Dotfiles für verschiedene Dinge wie Git und Travis CI, wir haben eine package.json Datei für den Generator selbst, a Readme Datei und einen Ordner für Tests. Außerdem erhält jeder Befehl in unserem Generator einen Ordner mit einem index.js Datei und einen Ordner für Vorlagen.

Das index.js Die Datei muss das eigentliche Generatorobjekt exportieren, das von Yeoman ausgeführt wird. Ich werde alles im eigentlichen Generator löschen, damit wir von vorne anfangen können. So sollte die Datei danach aussehen:

'streng verwenden'; var util = erfordern ('util'); var path = required ('pfad'); var yeoman = erfordern ('yeoman-generator'); var Kreide = erfordern ('Kreide'); var OnepageGenerator = yeoman.generators.Base.extend (); module.exports = OnepageGenerator; 

Ein Yeoman Generator kann aus zwei verschiedenen vorgefertigten Optionen bestehen: der Base Generator, von dem Sie sehen können, dass er erbt, oder die NamedBase generator, was eigentlich dasselbe ist, außer dass ein einzelner Parameter hinzugefügt wird, den der Benutzer beim Aufruf des Generators einstellen kann. Es ist nicht so wichtig, welchen Sie wählen, Sie können Parameter manuell hinzufügen, wenn Sie mehr benötigen.

Dieser Code erstellt lediglich dieses Generatorobjekt und exportiert es. Yeoman ruft das exportierte Objekt tatsächlich ab und führt es aus. Die Art und Weise, wie sie ausgeführt wird, besteht darin, zuerst die Konstruktormethode aufzurufen, um das Objekt einzurichten. Anschließend durchläuft es alle Methoden, die Sie für dieses Objekt erstellen (in der Reihenfolge, in der Sie sie erstellt haben) und führen sie nacheinander aus. Das heißt, es spielt keine Rolle, wie Sie die Funktionen bezeichnen, und Sie haben die Flexibilität, ihnen Dinge zu nennen, die für Sie sinnvoll sind.

Das andere, was Sie wissen sollten, ist, dass Yeoman über eigene Funktionen für den Umgang mit dem Dateisystem verfügt, die Ihnen bei Pfaden wirklich helfen. Alle Funktionen, die Sie normalerweise verwenden würden mkdir, lesen, schreiben, Kopieren, usw wurden bereitgestellt, aber Yeoman verwendet das Vorlagenverzeichnis im Ordner dieses Befehls als Pfad zum Lesen von Daten und den Ordner, in dem der Benutzer den Generator ausführt, als Stammpfad für die Ausgabe von Dateien. Dies bedeutet, dass Sie sich beim Arbeiten mit Dateien nicht einmal über die vollständigen Pfade Gedanken machen müssen. Sie können lediglich eine Kopie ausführen, und yeoman wird die zwei verschiedenen Speicherorte für Sie übernehmen.

Input erhalten

Mit Yeoman können Sie Ihrem Generator Fragen hinzufügen, sodass Sie Eingaben vom Benutzer erhalten und das Verhalten entsprechend anpassen können. Wir werden zwei Fragen in unserem Generator haben. Die erste ist der Name des Projekts und die zweite ist, ob als Beispiel ein Dummy-Abschnitt eingefügt werden soll oder nicht.

Um dies zu erreichen, fügen wir eine Funktion hinzu, die den Benutzer zur Eingabe dieser beiden Informationen auffordert und die Ergebnisse dann in unserem Objekt selbst speichert:

var OnepageGenerator = yeoman.generators.Base.extend (promptUser: function () var done = this.async (); // lässt Yeoman den Benutzer console.log (this.yeoman) begrüßen; var frompts = [name: 'appName', Nachricht: 'Wie lautet der Name Ihrer App?', Typ: 'Confirm', Name: 'AddDemoSection', Nachricht: 'Möchten Sie einen Demo-Abschnitt generieren?', Standard: true ]; this.prompt (Eingabeaufforderungen, Funktion (Requisiten) this.appName = props.appName; this.addDemoSection = props.addDemoSection; done (); .bind (this));; 

Die erste Zeile innerhalb der Funktion setzt a erledigt Variable aus dem Objekt asynchron Methode. Yeoman versucht, Ihre Methoden in der Reihenfolge auszuführen, in der sie definiert sind. Wenn Sie jedoch einen asynchronen Code ausführen, wird die Funktion beendet, bevor die eigentliche Arbeit abgeschlossen ist, und Yeoman startet die nächste Funktion vorzeitig. Um dies zu umgehen, können Sie die anrufen asynchron Diese Methode gibt einen Rückruf zurück. Anschließend wartet Yeoman mit der nächsten Funktion, bis der Rückruf ausgeführt wird, was Sie am Ende nach der Aufforderung des Benutzers sehen können.

Die nächste Zeile, in der wir uns gerade anmelden Yeoman, Das ist das Yeoman-Logo, das Sie gesehen haben, als wir kurz zuvor den Yeoman-Generator ausgeführt haben. Als Nächstes haben wir eine Liste von Eingabeaufforderungen definiert. Jede Eingabeaufforderung hat einen Typ, einen Namen und eine Nachricht. Wenn kein Typ angegeben wurde, wird standardmäßig 'Eingabe' verwendet, was für die Standardtexteingabe gilt. Es gibt viele coole Eingabetypen wie Listen, Kontrollkästchen und Passwörter. Sie können die vollständige Liste hier anzeigen. In unserer Anwendung verwenden wir eine Texteingabe- und eine Bestätigungsart (Ja / Nein).

Wenn die Reihe der Fragen fertig ist, können wir sie an das Forum weiterleiten Prompt Methode zusammen mit einer Rückruffunktion. Der erste Parameter der Rückruffunktion ist die Liste der vom Benutzer zurückgegebenen Antworten. Wir hängen diese Eigenschaften dann an unser Hauptobjekt an und nennen das erledigt Methode, um zur nächsten Funktion zu gelangen.

Gerüste Unsere Anwendung

Unsere Anwendung hat das äußere Skelett, das die Kopfzeile, das Menü, die Fußzeile und ein paar CSS-Dateien enthält. Anschließend haben wir den inneren Inhalt, der in einem eigenen Verzeichnis mit einer benutzerdefinierten CSS-Datei pro Abschnitt gespeichert wird. Auf diese Weise können Sie globale Eigenschaften in der Hauptdatei festlegen und jede Zeile einzeln anpassen.

Beginnen wir mit der Header-Datei. Erstellen Sie einfach eine neue Datei im Vorlagenverzeichnis _header.html mit den folgenden:

    <%= site_name %>    

Die Dateinamen müssen nicht mit einem Unterstrich beginnen. Es empfiehlt sich jedoch, den Namen vom endgültigen Ausgabenamen zu unterscheiden, damit Sie feststellen können, mit welchem ​​Sie sich befassen. Außerdem können Sie sehen, dass ich neben der Einbindung unserer CSS-Hauptdatei auch die Semantic UI Library als Raster für Zeilen und für das Menü mit einbezieht (ganz zu schweigen von der guten Gestaltung)..

Eine andere Sache, die Sie vielleicht bemerkt haben, ist, dass ich für den Titel einen EJS-artigen Platzhalter verwendet habe, der zur Laufzeit ausgefüllt wird. Yeoman verwendet Underscore-Templates (gut 10 Strich) und so können Sie Ihre Dateien so erstellen und die Daten werden zur Laufzeit generiert.

Als Nächstes erstellen wir die Fußzeile (setzen Sie diese in eine Datei mit dem Namen) _footer.html im Vorlagenordner):

  

Es schließt nur das HTML-Dokument für uns, da in unserer Anwendung kein JavaScript vorhanden ist. Die letzte HTML-Datei, die für das äußere Gerüst erforderlich ist, ist das Menü. Wir werden zur Laufzeit die eigentlichen Links generieren, aber wir können den äußeren Container in eine als Vorlagendatei bezeichnete Datei schreiben _menu.html:

Es ist ein einfaches Div mit einigen Klassen, die von Semantic UI bereitgestellt werden. Wir werden die tatsächlichen Links später basierend auf den generierten Abschnitten ziehen. Als nächstes machen wir das _main.css Datei:

body margin: 0; Schriftfamilie: "Open Sans", "Helvetica Neue", "Helvetica", "Arial", Serifenlose;  

Nur ein wenig Code, der bei der Menüpositionierung und beim Ändern der Schriftart behilflich ist. Hier können Sie beliebige Standardstile für das gesamte Dokument einfügen. Jetzt müssen wir Vorlagen für die einzelnen Abschnitte und die zugehörigen CSS-Dateien erstellen. Dies sind ziemlich grundlegende Dateien, da wir sie für den Benutzer ziemlich leer lassen, so dass sie vom Benutzer angepasst werden können _section.html Folgendes hinzufügen:

<%= content %>

Nur ein äußeres Div mit einer eindeutigen ID, die wir basierend auf dem Namen des Abschnitts und einer Klasse für das Semantic-Grid-System generieren werden. Dann fügte ich innen einen hinzu H1 Tag, der wieder nur den Namen des Abschnitts enthält. Als Nächstes erstellen wir die CSS-Datei. Erstellen Sie also eine Datei mit dem Namen section.css mit den folgenden:

#<%= id %>Hintergrund: #FFFFFF; Farbe: # 000;  

Dies ist eher ein Platzhalter mit der gleichen ID wie der äußere Container. Es ist jedoch üblich, den Hintergrund jeder Zeile zu ändern, um den Inhalt zu trennen. Daher habe ich diese Eigenschaft aus Gründen der Übersichtlichkeit hinzugefügt. Nun, nur der Vollständigkeit halber, erstellen wir eine Datei namens _gruntfile.js aber wir werden es später bevölkern und das ausfüllen _package.json Datei, die bereitgestellt wurde:

"name": "appname", "version": "0.0.0", "devDependencies": "grunt": "~ 0.4.2", "grunt-contrib-connect": "~ 0.6.0", "grunt-contrib-concat": "~ 0.3.0", "grunt-contrib-cssmin": "~ 0.7.0" 

Es ist ein bisschen ein Spoiler, was wir später tun werden, aber es sind die Abhängigkeiten, die wir für unsere Grunt-Aufgaben benötigen.

Wenn alle diese Dateien bereit sind, fügen wir die Methoden hinzu, um ein neues Projekt zu erstellen. Also zurück in unsere index.js Datei direkt nach dem promptUser Methode, fügen wir eine Funktion hinzu, um alle Ordner zu erstellen, die wir benötigen:

scaffoldFolders: function () this.mkdir ("app"); this.mkdir ("app / css"); this.mkdir ("app / abschnitte"); this.mkdir ("build"); , 

Ich fügte ein hinzu App Ordner und innen zwei weitere Verzeichnisse: css und Abschnitte, Hier würde der Endanwender eine Anwendung erstellen. Ich habe auch eine erstellt bauen Ordner, in dem die verschiedenen Abschnitte und CSS-Dateien zusammengestellt und in eine einzige Datei integriert werden.

Als Nächstes fügen Sie eine weitere Methode hinzu, um einige unserer Vorlagen zu kopieren:

copyMainFiles: function () this.copy ("_ footer.html", "app / footer.html"); this.copy ("_ gruntfile.js", "Gruntfile.js"); this.copy ("_ package.json", "package.json"); this.copy ("_ main.css", "app / css / main.css"); var context = site_name: this.appName; this.template ("_ header.html", "app / header.html", Kontext); , 

Hier verwenden wir zwei neue Methoden, Kopieren und Vorlage, welche in Funktion ziemlich ähnlich sind. Kopieren nimmt die Datei aus dem Vorlagenverzeichnis und verschiebt sie unter Verwendung der angegebenen Pfade in den Ausgabeordner. Vorlage tut dasselbe, es sei denn, es wird vor dem Schreiben in den Ausgabeordner mit der Kontextfunktion von Underscore zusammen mit dem Kontext ausgeführt, um die Platzhalter auszufüllen.

Ich habe das Menü noch nicht kopiert, da wir die Links generieren müssen, aber wir können die Links erst erstellen, wenn wir den Demo-Abschnitt hinzugefügt haben. Die nächste Methode, die wir erstellen, fügt den Demo-Abschnitt hinzu:

generateDemoSection: function () if (this.addDemoSection) var context = content: "Demo-Bereich", id: this ._. classify ("Demo-Abschnitt") var fileBase = Date.now () + "_" + this ._. unterstrichen ("Demo Section"); var htmlFile = "app / section /" + fileBase + ".html"; var cssFile = "app / css /" + fileBase + ".css"; this.template ("_ section.html", htmlFile, Kontext); this.template ("_ section.css", cssFile, context); , 

Eine andere Funktion, mit der Sie möglicherweise nicht vertraut sind, ist die klassifizieren Funktion, die Ihnen von Underscore Strings zur Verfügung gestellt wird. Was er tut, ist, dass er eine Zeichenfolge benötigt und eine „Klassenversion“ davon erstellt. Er entfernt beispielsweise Leerzeichen und erstellt eine Version mit Kamelgehäuse, die sich für Dinge wie HTML-Klassen und IDs eignet. unterstrichen tut dasselbe, außer dass sie statt Kamelhüllen sie schlängelt. Abgesehen davon ist es alles, was wir in der vorherigen Funktion gemacht haben, das einzige andere, das erwähnenswert ist, ist, dass wir einen Zeitstempel vorbereiten, sowohl um die Dateien eindeutig zu halten, als auch um zu bestellen. Wenn wir die Dateien laden, werden sie alphabetisch geordnet, so dass die Zeit als Präfix sie in Ordnung hält.

Die letzte zu kopierende Datei ist die menu.html Datei, aber dafür müssen wir die Links generieren. Das Problem ist, dass wir zu diesem Zeitpunkt nicht wirklich wissen, welche Dateien zuvor erstellt wurden oder ob der Benutzer Abschnitte manuell hinzugefügt hat. Um das Menü aufzubauen, müssen wir aus dem Ausgabepfad lesen und nachdem wir die dort vorhandenen Abschnitte gelesen haben, müssen wir die Menüverknüpfungen erstellen. Es gibt eine Handvoll neuer Funktionen, die wir noch nicht verwendet haben. Wir werden sie Zeile für Zeile durchgehen:

generateMenu: function () var menu = this.read ("_ menu.html"); var t = '<%= name %>'; var files = this.expand ("app / section / *. html"); für (var i = 0; i < files.length; i++)  var name = this._.chain(files[i]).strRight("_").strLeftBack(".html").humanize().value(); var context =  name: name, id: this._.classify(name) ; var link = this.engine(t, context); menu = this.append(menu, "div.menu", link);  this.write("app/menu.html", menu); , 

Die erste Zeile liest den äußeren HTML-Code des Menüs, und dann habe ich eine Vorlagenzeichenfolge erstellt, die wir für jeden Link verwenden können. Danach benutzte ich die erweitern Funktion, die einen Ressourcenpfad akzeptiert, relativ zu dem Ordner, in dem der Generator aufgerufen wurde (Ausgabepfad), und gibt ein Array aller Dateien zurück, die dem angegebenen Muster entsprechen. In unserem Fall werden alle HTML-Abschnittsdateien zurückgegeben.

Dann durchlaufen wir die Liste der Dateien, und für jede Datei verwenden wir Underscore Strings, um den Zeitstempel und die Dateierweiterung zu entfernen, sodass nur noch der Dateiname übrig bleibt. Das vermenschlichen Die Methode nimmt Dinge, die sich im Kamelgehäuse befinden, oder Zeichen wie Unterstriche und Bindestriche und konvertiert sie in Leerzeichen, sodass Sie eine für Menschen lesbare Zeichenfolge erhalten. Außerdem wird der erste Buchstabe der Zeichenfolge groß geschrieben, der sich für unser Menü als großartig erweist. Wenn der Name getrennt ist, erstellen wir ein Kontextobjekt mit der zuvor verwendeten ID, sodass die Links uns tatsächlich zu den richtigen Abschnitten führen.

Jetzt haben wir zwei neue Funktionen, die wir noch nicht gesehen haben, Motor und anhängen. Motor Nimmt eine Vorlagenzeichenfolge als ersten Parameter und ein Kontextobjekt als zweite, führt sie durch die Templating Engine und gibt die Ergebnisse zurück. anhängen akzeptiert einen HTML-String als ersten Parameter, einen Selektor als zweiten und etwas, das als dritter Parameter eingefügt werden soll. In diesem Fall wird der bereitgestellte Inhalt am Ende aller Elemente eingefügt, die mit dem Selektor übereinstimmen. Hier fügen wir den Link am Ende des Speisekarte div.

Zu guter Letzt haben wir die schreiben Eine Methode, die unseren berechneten HTML-String in die Datei schreibt. Um hier fertig zu sein, fügen wir eine Methode hinzu, die ausgeführt werden soll npm:

runNpm: function () var done = this.async (); this.npmInstall ("", function () console.log ("\ nAlles Setup !!! \ n"); done (););  

Der erste Parameter für npmInstall ist die Pfade, aber Sie können dieses Feld einfach leer lassen, außerdem drucke ich am Ende eine Nachricht aus und sage dem Benutzer, dass die App bereit ist.

Kurz gesagt: Wenn unser Generator läuft, werden Sie nach dem Namen des Projekts gefragt und gefragt, ob Sie einen Demo-Abschnitt hinzufügen möchten. Danach erstellt es die Ordnerstruktur und kopiert alle für unser Projekt benötigten Dateien. Sobald es fertig ist, wird es ausgeführt npm Um die von uns definierten Abhängigkeiten zu installieren, wird die gerade eingeblendete Nachricht angezeigt.

Dies ist so ziemlich alles, was der Hauptgenerator tun muss, aber ohne die Grunt-Aufgaben ist er nicht so nützlich. Derzeit sind es nur ein paar separate Dateien, aber um die Abschnitte richtig zu entwickeln, benötigen wir eine Möglichkeit, sie als einzelne Datei in der Vorschau anzuzeigen, und wir müssen auch die Ergebnisse erstellen. So offen _gruntfile.js aus dem Vorlagen-Verzeichnis und los geht's:

module.exports = function (grunt) grunt.initConfig (// task config); grunt.loadNpmTasks ('grunt-contrib-connect'); grunt.loadNpmTasks ('grunt-contrib-cssmin'); grunt.loadNpmTasks ('grunt-contrib-concat'); grunt.registerTask ('dienen', ['verbinden']); grunt.registerTask ('build', ['concat', 'cssmin']); grunt.registerTask ('default', ['build']); ; 

Bisher ist dies nur die Standardkesselplatte, wir müssen eine Funktion exportieren und in ihr sind die drei Abhängigkeiten enthalten, die wir hinzugefügt haben package.json Datei, und dann registrieren wir die Aufgaben. Wir werden eine haben Dienen Aufgabe, die sich wie ein Server verhält und uns die getrennten Dateien anzeigen lässt, während wir unsere Website entwickeln, und dann haben wir die bauen Befehl, der alle HTML- und CSS-Elemente in unserer App für die Bereitstellung zusammenfasst. Ich habe auch die letzte Zeile hinzugefügt, also wenn du nur rennst grunzen von selbst wird davon ausgegangen, dass Sie bauen wollen.

Beginnen wir nun mit der Konfiguration für die bauen Befehl, da es viel kürzer ist:

concat: dist: src: ["app / header.html", "app / menu.html", "app / section / *. html", "app / footer.html"], dest: "build / index .html ", cssmin: css: files: " build / css / main.css ": [" app / css / *. css "] 

Beim HTML-Code werden alle HTML-Dateien in der angegebenen Reihenfolge im Build-Ordner unter dem Namen zusammengefügt index.html. Mit dem CSS wollen wir es auch minimieren, also benutzen wir das cssmin Plugin und wir spezifizieren, dass wir eine Datei namens ausgeben wollen main.css in der build / css Ordner und wir möchten, dass die minimierten Versionen aller CSS-Dateien in unserem Projekt enthalten sind.

Server verbinden

Als Nächstes müssen wir die Konfiguration für den Connect-Server hinzufügen. Connect bietet eine Menge großartiger Middleware für das Bereitstellen statischer Dateien oder Verzeichnisse. Hier müssen wir jedoch zuerst alle Dateien kombinieren, sodass einige benutzerdefinierte Handler erstellt werden müssen. Beginnen wir mit der Basiskonfiguration:

connect: server: options: keepalive: true, open: true, Middleware: function () // benutzerdefinierte Handler hier, 

Das bleib am Leben Mit dieser Option wird Grunt angewiesen, den Server weiter auszuführen öffnen Diese Option weist Grunt an, die URL in Ihrem Browser automatisch zu öffnen, wenn Sie den Server starten (dies ist jedoch eher eine persönliche Präferenz) und Middleware Funktion soll ein Array von Middleware-Handlern zur Verarbeitung der Anforderungen zurückgeben.

In unserer Anwendung gibt es im Wesentlichen zwei Ressourcen, die zu behandeln sind, die Stammressource (unserindex.html”) In diesem Fall müssen wir alle unsere HTML-Dateien zusammenfassen und zurückgeben.main.css”Ressource, in diesem Fall möchten wir alle CSS-Dateien zusammen zurückgeben. Für alles andere können wir einfach eine 404 zurückgeben.

Beginnen wir mit der Erstellung eines Arrays für die Middleware und fügen Sie das erste hinzu (dies geht in die Middleware Eigenschaftsfunktion, wo ich den Kommentar abgelegt habe):

 var Middleware = []; middleware.push (Funktion (req, res, next) if (req.url! == "/") return next (); res.setHeader ("Inhaltstyp", "text / html"); var html = grunt.file.read ("app / header.html"); html + = grunt.file.read ("app / menu.html"); var files = grunt.file.expand ("app / section / *. html.) "); für (var i = 0; i < files.length; i++)  html += grunt.file.read(files[i]);  html += grunt.file.read("app/footer.html"); res.end(html); ); 

Wir haben uns irgendwie von Yeomans API zu Grunt's gewandelt, aber zum Glück sind die Befehlsnamen fast identisch, wir verwenden sie immer noch lesen um Dateien zu lesen und erweitern um die Liste der Dateien zu erhalten. Außerdem legen wir nur den Inhaltstyp fest und geben die verkettete Version aller HTML-Dateien zurück. Wenn Sie mit einem Middleware-basierten Server nicht vertraut sind, wird der Middleware-Stack grundsätzlich durchlaufen, bis irgendwo in der Zeile eine Funktion die Anforderung verarbeiten kann.

In der ersten Zeile prüfen wir, ob es sich bei der Anfrage um die Stamm-URL handelt, ob wir die Anfrage bearbeiten, ansonsten rufen wir auf Nächster() Dadurch wird die Anforderung an die nächste Middleware weitergeleitet.

Als nächstes müssen wir die Anfrage an übergeben /css/main.css Wenn Sie sich erinnern, haben wir es in der Kopfzeile eingerichtet:

 middleware.push (Funktion (req, res, next) if (req.url! == "/css/main.css") return next (); res.setHeader ("Inhaltstyp", "text / css") ); var css = ""; var files = grunt.file.expand ("app / css / *. css"); für (var i = 0; i < files.length; i++)  css += grunt.file.read(files[i]);  res.end(css); ); 

Im Grunde ist dies die gleiche Funktion wie zuvor, mit Ausnahme der CSS-Dateien. Zu guter Letzt werde ich eine 404-Nachricht hinzufügen und den Middleware-Stack zurückgeben:

 middleware.push (Funktion (req, res) res.statusCode = 404; res.end ("Not Found");); Middleware zurückgeben; 

Das bedeutet, wenn Anforderungen von den beiden vorherigen Funktionen nicht verarbeitet werden, senden wir diese 404-Nachricht. Und das ist alles, was dazu gehört, wir haben den Generator, der das Projekt und die Grunt-Aufgaben erstellen wird, die es uns ermöglichen, eine Vorschau unserer App zu erstellen und sie zu erstellen. Das letzte Thema, das ich kurz behandeln möchte, sind Subgeneratoren.

Untergeneratoren

Wir haben bereits den Hauptgenerator erstellt, mit dem unsere Anwendung erstellt wird. Mit Yeoman können Sie jedoch so viele Subgeneratoren erstellen, wie Sie nach dem ersten Gerüst verwenden möchten. In unserer Anwendung benötigen wir einen, um neue Abschnitte zu generieren. Um zu beginnen, können wir tatsächlich einen Subgenerator aus der Generator: Generator Um die Datei zu scaffoldieren, führen Sie dazu einfach den folgenden Befehl in unserem aus eine Seite Mappe:

yo generator: subgenerator section 

Dadurch wird ein neuer Ordner mit dem Namen erstellt Sektion mit einem index.js Datei und a Vorlagen Ordner wie unser Hauptgenerator (App). Lassen Sie den Generator wie beim letzten Mal leer und Sie sollten mit den folgenden Informationen rechnen:

'streng verwenden'; var util = erfordern ('util'); var yeoman = erfordern ('yeoman-generator'); var SectionGenerator = yeoman.generators.NamedBase.extend (); module.exports = SectionGenerator; 

Möglicherweise stellen Sie auch fest, dass wir aus dem NamedBase nicht das regelmäßige Base, Das bedeutet, dass Sie beim Aufruf des Generators einen Namensparameter angeben müssen, auf den Sie dann mit zugreifen können dieser Name. Nun ist dieser Code im Wesentlichen die zwei Funktionen, die wir bereits im vorherigen Generator geschrieben haben: generateDemoSection und generateMenu, So können wir diese beiden Funktionen hier einfach kopieren. Ich werde den Namen des ersten durch etwas passenderes ersetzen:

generateSection: function () var context = content: this.name, id: this ._. classify (this.name) var fileBase = Date.now () + "_" + dieser ._. unterstrichen (this. Name); var htmlFile = "app / section /" + fileBase + ".html"; var cssFile = "app / css /" + fileBase + ".css"; this.template ("_ section.html", htmlFile, Kontext); this.template ("_ section.css", cssFile, context); , generateMenu: function () var menu = this.read ("_ menu.html"); var t = '<%= name %>'; var files = this.expand ("app / section / *. html"); für (var i = 0; i < files.length; i++)  var name = this._.chain(files[i]).strRight("_").strLeftBack(".html").humanize().value(); var context =  name: name, id: this._.classify(name) ; var link = this.engine(t, context); menu = this.append(menu, "div.menu", link);  this.write("app/menu.html", menu);  

Der einzige Unterschied ist, anstatt den Namen direkt einzugeben, verwende ich das dieser Name Eigentum. Die einzige andere Sache ist, dass wir die Vorlagendateien verschieben müssen _sections.html, _section.css und _menu.html von App / Vorlagen zu Abschnitt / Vorlagen da ist das wo Vorlage/lesen Befehle werden aussehen.

Code-Duplizierung

Das Problem ist jetzt die Code-Duplizierung. Also zurück in die app / index.js Datei, anstatt den gleichen Code wie der Subgenerator zu haben, können wir einfach den Subgenerator aufrufen und ihm das Namensargument übergeben. Sie können entfernen generateMenu von app / index.js zusammen und wir werden ändern generateDemoSection Zu dem Folgendem:

generateDemoSection: function () if (this.addDemoSection) var done = this.async (); this.invoke ("onepage: section", args: ["Demo Section"]), function () done (););  else this.write ("app / menu.html", ""); , 

Wenn der Benutzer also den Demo-Abschnitt erstellen wollte, dann wir aufrufen Der Subgenerator übergibt das erste Argument, dh den Namen. Wenn der Benutzer den Demo-Beitrag nicht wollte, müssen wir noch etwas für das Menü erstellen, da unsere Grunt-Tasks es versuchen und lesen werden. Deshalb schreiben wir im letzten Abschnitt einen leeren String in die Menüdatei.

Unser Generator ist endlich fertig und wir können es jetzt testen.

Testen Sie es heraus

Als Erstes müssen wir unseren Generator in Ihrem System installieren. Anstatt den Edelstein regelmäßig zu installieren, können wir das verwenden Verknüpfung Mit diesem Befehl können Sie den Ordner einfach mit dem Edelsteinpfad verknüpfen. Auf diese Weise können Sie hier weiterhin Änderungen vornehmen, ohne ihn jedes Mal neu installieren zu müssen. Führen Sie einfach das Projektverzeichnis aus nom link.

Der letzte Schritt ist, den Generator tatsächlich laufen zu lassen. Erstellen Sie einfach einen neuen Ordner und führen Sie den Vorgang aus Ihre Seite Dadurch wird unser Hauptgenerator ausgeführt und die Abhängigkeiten über installiert npm. Wir können dann unsere Grunt-Aufgaben verwenden, um die Seite anzuzeigen (grunzen dienen) oder fügen Sie einige Abschnitte mit unserem Generator hinzu yo onpage: Abschnitt "Ein anderer Abschnitt" und die neuen Dateien werden hinzugefügt.

Fazit

In diesem Artikel haben wir viele der allgemeinen Funktionen behandelt, aber es gibt noch weitere interessante Optionen zum Auschecken. Wie Sie wahrscheinlich feststellen können, ist beim Erstellen eines Generators ein gewisses Maß an Boilerplate erforderlich, aber das ist eine Frage des Punktes. Sie erledigen das alles einmal und können es dann in allen Ihren Anwendungen nutzen.

Ich hoffe, Ihnen hat es gefallen, diesen Artikel zu lesen. Wenn Sie Fragen oder Anmerkungen haben, können Sie sie gerne weiter unten lesen.