In diesem Beitrag beschreibe ich meine Erfahrungen mit dem Aufbau einer modernen Front-End-Pipeline in Rails.
Zuerst habe ich ein leeres Rails-Projekt erstellt und Webpacker hinzugefügt. Webpacker ist ein offizielles Gem und seit Version 5.1 in Rails enthalten. Es fungiert als Wrapper um Webpack, einen JavaScript-Modul-Bundler. Ich würde es später verwenden, um meine JavaScript-Build-Pipeline zu definieren, einschließlich Linting, Transpilierung und Minimierung. Alle zusätzlichen Node-Pakete, die über yarn oder npm installiert werden, müssten ebenfalls per Webpack eingebunden werden (oder direkt im Quellcode mithilfe von ES6-Importanweisungen).
Es gibt zwei Möglichkeiten, Webpacker in ein Projekt einzubinden. Wenn man ein neues Projekt erstellt, muss man lediglich das Flag –webpack
zu dem rails new
Shell-Befehl hinzufügen, z.B.
$ rails new myapp –webpack
Da ich aber bereits ein Projekt angelegt habe und es nicht löschen und von vorne anfangen wollte, habe ich eine alternative Methode verwendet. Diese war etwas ausführlicher, führte aber zum gleichen Ergebnis. Als erstes habe ich den folgenden Eintrag zu dem Gemfile in meinem Projekt hinzugefügt:
gem 'webpacker', '~> 3.5'
Dann habe ich die folgenden Shell-Befehle ausgeführt:
$ bundle $ rails webpacker:install
Zu diesem Zeitpunkt hatte ich bereits ein sehr einfaches, funktionierendes Setup. Das Ausführen der oben aufgeführten Befehle installierte Babel, einen Transpiler, legte die Datei application.js
an und platzierte sie in dem neu erstellten Ordner app/javascript/packs
. Ich konnte jetzt mit dem Schreiben vom modernen, ES6-konformen JavaScript-Code beginnen, in dem ich Module in dem Ordner app/javascript
ablegte und sie anschließend in dem app/javascript/packs/application.js
Bootstrapping-Skript importiere.
Um den Code tatsächlich in den endgültigen Build aufzunehmen, war ein weiterer Schritt erforderlich. Ich musste das folgende Tag am Ende des HEAD
-Abschnitts des Hauptanwendungslayouts unter app/views/layouts/application.html.erb
hinzufügen:
<%= javascript_pack_tag 'application' %>
Erwähnenswert ist die Tatsache, dass die Standard-Asset-Pipeline derzeit noch besteht und fröhlich vor sich hin tuckert. Durch die Installation von Webpack und das Hinzufügen des obigen Tags zu meinem Anwendungslayout habe ich nur eine weitere JavaScript-Datei hinzugefügt, die im endgültigen Build enthalten sein wird. Dieser Workflow ist nicht destruktiv und ermöglicht es Webpack-Neulingen, mit seiner Funktionalität in vorhandenen Projekten zu experimentieren, ohne dass eine Refaktorisierung erforderlich ist. Darüber hinaus kann die Asset-Pipeline weiterhin für die meisten Anforderungen genutzt werden und Webpack kann nur verwendet werden, um den JavaScript-Kompilierungsvorgang zu ersetzen, um von allen seinen modernen Funktionen zu profitieren. Genau das habe ich beabsichtigt, und da ich nicht vorhatte, JavaScript-Code mit der Standard-Asset-Pipeline zu kompilieren, entfernte ich auch das folgende Tag aus dem Anwendungslayout:
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
An dieser Stelle fiel mir auf, dass async/await – eins meiner neuen Lieblingsfeatures von JavaScript – nicht funktionierte, obwohl laut der Webpacker-Dokumentation die Unterstützung in der Standardinstallation enthalten sein müsste. Nachdem ich ein bisschen recherchiert habe, konnte ich das Problem aber beheben, indem ich das Transform Runtime – Plugin für Babel installiert habe:
$ yarn add babel-plugin-transform-runtime
Nachdem die Einrichtung für meinen Transpiler abgeschlossen war, war es an der Zeit, der Pipeline einen Linter hinzuzufügen. Es gab mehrere Auswahlmöglichkeiten und aufgrund persönlicher Präferenz habe ich mich für ESLint entschieden.
Ich habe ESLint installiert und die Setup-Routine ausgeführt, um eine erste Konfigurationsdatei zu erstellen (.eslintrc.js
):
$ yarn add -D eslint eslint-loader $ yarn run eslint –init
Um meinen Code auf Fehler zu überprüfen, konnte ich nun den folgenden Befehl in der Shell ausführen:
$ yarn eslint app/javascript
Das war nett, aber eigentlich wollte ich sicherstellen, dass der Linter automatisch ausgeführt wird, wenn mein Code neu kompiliert wird. Dazu musste ich die folgenden Zeilen in config/webpack/environment.js
einfügen (vor der Zeile module.exports = environment;
):
environment.loaders.append('eslint', { enforce: 'pre', test: /\.js$/, use: 'eslint-loader', exclude: /node_modules/ });
Das Letzte, was ich auf meiner Liste abhaken musste, war ein Front-End-Framework. In der Vergangenheit habe ich mit Angular und Ember gearbeitet, mein aktuelles Framework der Wahl war aber Vue. Glücklicherweise war das Hinzufügen von Vue zu dem Projekt dank Webpacker so einfach wie das Ausführen eines einzelnen Shell-Befehls:
$ rails webpacker:install:vue
Uuund ich war fertig. Oder sollte ich lieber sagen, fast fertig. Bei der Standardinstallation wurden einige Beispieldateien hinzugefügt, die ich nicht benötigte. Außerdem unterscheidet sich die Art und Weise, wie ich meine Vue-Hauptinstanz auf einer Seite gern einrichte, von der standardmäßig bereitgestellten. Und zu guter Letzt musste ich ESLint mitteilen, dass es auch alle Vue-Einzeldateikomponenten parsen soll, auf die es im Ordner app/javascript
stoßen würde. Momentan würde ESLint jede Datei ohne die Erweiterung .js
ignorieren und nicht wissen, wie Vue-Einzeldateikomponenten zu behandelt wären.
Zuerst habe ich die Dateien gelöscht, die ich nicht brauchte. Die Dateien app.vue
im Ordner app/javascript
und hello_vue.js
in app/javascript/packs
landeten beide im Papierkorb.
Als Nächstes habe ich den folgenden Code in die Datei app/javascript/packs/application.js
eingefügt:
import Vue from 'vue/dist/vue.esm.js'; const app = new Vue({ el: '#app', });
Ich habe auch das Layout der Hauptanwendung bearbeitet, indem ich den Parameter :defer = true
zu dem zuvor dort eingefügten Tag hinzugefügt habe:
<%= javascript_pack_tag 'application', :defer = true %>
Das Hinzufügen des defer
-Attributs würde sicherstellen, dass mein JavaScript-Code erst ausgeführt wird, nachdem die Seite vollständig geladen wurde. Außerdem habe ich im Anwendungslayout den Inhalt zwischen den öffnenden und schließenden BODY
-Tags durch Folgendes ersetzt:
<div id="app"> <div class="container"> <%= yield %> </div> </div>
Schließlich habe ich das Vue-Plugin für ESLint installiert, indem ich den Shell-Befehl ausgeführt habe:
$ yarn add -D eslint-plugin-vue@next
Das Einzige, was noch zu tun blieb, war es ESLint und Webpacker anzuweisen, .vue
-Dateien zu parsen. Das tat ich, indem ich den Eintrag "plugin:vue/essential"
zum Array extends
in der Datei .eslintrc.js
hinzufügte:
module.exports = { ... "extends": [ "eslint:recommended", "plugin:vue/essential" ], ... };
Zu guter Letzt habe ich config/webpack/environment.js
aktualisiert:
environment.loaders.append('eslint', { enforce: 'pre', test: /\.js|\.vue$/, use: 'eslint-loader', exclude: /node_modules/ });
Und so weit ist das Grundsetup. Abhängig vom Projekt würde ich in den nächsten Schritten warscheinlich Axios für die Kommunikation mit einer API installieren und einrichten, Lodash oder Underscore für die zusätzliche Schweizer Taschenmesser-Funktionalität, die man manchmal braucht, oder Bootstrap und Fontawesome.
Aber das ist ein Thema für einen anderen Blogbeitrag.
Bartosz Holtgräwe, Entwickler Skysystems IT GmbH