Articles

Einrichten eines ES6-Projekts mit Babel und webpack

In diesem Artikel werden wir ein Build-Setup für die Verarbeitung von modernem JavaScript (das in Webbrowsern läuft) mit Babel und webpack erstellen.

Dies ist notwendig, um sicherzustellen, dass unser moderner JavaScript-Code mit einer größeren Anzahl von Browsern kompatibel ist, als dies sonst der Fall wäre.

JavaScript entwickelt sich, wie die meisten webbezogenen Technologien, ständig weiter. In der guten alten Zeit konnte man ein paar <script>-Tags in eine Seite einfügen, vielleicht noch jQuery und ein paar Plugins, und dann war man startklar.

Seit der Einführung von ES6 sind die Dinge jedoch immer komplizierter geworden. Die Browserunterstützung für neuere Sprachfeatures ist oft lückenhaft, und da JavaScript-Anwendungen immer anspruchsvoller werden, verwenden Entwickler zunehmend Module, um ihren Code zu organisieren. Das wiederum bedeutet, dass Sie, wenn Sie heute modernes JavaScript schreiben, einen Build-Schritt in Ihren Prozess einführen müssen.

Wie Sie aus den unten stehenden Links ersehen können, erhöht sich durch die Konvertierung von ES6 zu ES5 die Anzahl der von uns unterstützten Browser drastisch.

  • ES6-Kompatibilität
  • ES5-Kompatibilität

Der Zweck eines Build-Systems besteht darin, den Arbeitsablauf zu automatisieren, der erforderlich ist, um unseren Code für die Browser und die Produktion bereitzustellen. Dazu gehören Schritte wie das Transpilieren von Code in einen anderen Standard, das Kompilieren von Sass in CSS, das Bündeln von Dateien, das Minifizieren und Komprimieren von Code und viele andere. Um sicherzustellen, dass diese Schritte konsistent wiederholbar sind, wird ein Build-System benötigt, das die Schritte in einer bekannten Abfolge mit einem einzigen Befehl auslöst.

Voraussetzungen

Um mitmachen zu können, müssen Sie sowohl Node.js als auch npm installiert haben (sie werden im Paket geliefert). Ich würde empfehlen, einen Versionsmanager wie nvm zu verwenden, um Ihre Node-Installation zu verwalten (hier erfahren Sie, wie), und wenn Sie Hilfe benötigen, um mit npm zurechtzukommen, dann schauen Sie sich SitePoints einsteigerfreundliches npm-Tutorial an.

Einrichten

Erstellen Sie einen Stammordner irgendwo auf Ihrem Computer und navigieren Sie von Ihrem Terminal/Befehlszeile dorthin. Dies wird Ihr <ROOT>-Ordner sein.

Erstellen Sie eine package.json-Datei wie folgt:

npm init -y

Hinweis: Das -y-Flag erstellt die Datei mit Standardeinstellungen und bedeutet, dass Sie keine der üblichen Angaben in der Befehlszeile machen müssen. Sie können später in Ihrem Code-Editor geändert werden, wenn Sie dies wünschen.

In Ihrem Ordner <ROOT> erstellen Sie die Verzeichnisse src, src/js und public. Im Ordner src/js werden wir unseren unbearbeiteten Quellcode ablegen, und im Ordner public wird der transpilierte Code landen.

Transpilieren mit Babel

Um loszulegen, installieren wir babel-cli, das die Möglichkeit bietet, ES6 in ES5 zu transpilieren, und babel-preset-env, mit dem wir den transpilierten Code auf bestimmte Browserversionen ausrichten können.

npm install babel-cli babel-preset-env --save-dev

In der package.json-Datei sollte nun Folgendes zu sehen sein:

"devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.6.1"}

Wenn wir schon in der package.json-Datei sind, sollten wir den scripts-Abschnitt so ändern, dass er folgendermaßen lautet:

"scripts": { "build": "babel src -d public"},

Damit haben wir die Möglichkeit, Babel über ein Skript aufzurufen, anstatt jedes Mal direkt über das Terminal. Wenn Sie mehr über npm-Skripte und ihre Möglichkeiten erfahren möchten, sehen Sie sich dieses SitePoint-Tutorial an.

Zuletzt müssen wir noch eine .babelrcKonfigurationsdatei erstellen, bevor wir testen können, ob Babel seine Aufgabe erfüllt. Auf diese Datei bezieht sich unser babel-preset-env-Paket für seine Transpile-Parameter.

Erstellen Sie in Ihrem <ROOT>-Verzeichnis eine neue Datei mit dem Namen .babelrc und fügen Sie Folgendes ein:

{ "presets": } } ] ]}

Damit wird Babel so eingerichtet, dass es für die letzten beiden Versionen jedes Browsers sowie für Safari mit Version 7 oder höher transpiliert. Andere Optionen sind verfügbar, je nachdem, welche Browser Sie unterstützen müssen.

Nachdem das gespeichert ist, können wir die Dinge mit einer Beispiel-JavaScript-Datei testen, die ES6 verwendet. Für diesen Artikel habe ich eine Kopie von leftpad so modifiziert, dass die ES6-Syntax an einigen Stellen verwendet wird: Template-Literale, Pfeilfunktionen, const und let.

Speichern Sie diese Datei als src/js/leftpad.js und führen Sie im Terminal Folgendes aus:

npm run build

Wenn alles wie gewünscht funktioniert, sollten Sie in Ihrem Ordner public nun eine neue Datei namens js/leftpad.js finden. Wenn Sie diese Datei öffnen, werden Sie feststellen, dass sie keine ES6-Syntax mehr enthält und wie folgt aussieht:

Organizing Your Code with ES6 Modules

Ein ES6-Modul ist eine JavaScript-Datei, die Funktionen, Objekte oder primitive Werte enthält, die Sie einer anderen JavaScript-Datei zur Verfügung stellen wollen. Sie export aus der einen und import in die andere. Jedes ernsthafte moderne JavaScript-Projekt sollte die Verwendung von Modulen in Betracht ziehen. Sie ermöglichen es Ihnen, Ihren Code in in sich geschlossene Einheiten aufzuteilen und damit die Wartung zu vereinfachen; sie helfen Ihnen, Namespace-Verschmutzung zu vermeiden; und sie machen Ihren Code portabler und wiederverwendbar.

Während der Großteil der ES6-Syntax in modernen Browsern weithin verfügbar ist, ist dies bei Modulen noch nicht der Fall. Zum Zeitpunkt der Erstellung dieses Artikels sind sie in Chrome, Safari (einschließlich der neuesten iOS-Version) und Edge verfügbar; in Firefox und Opera sind sie hinter einer Flagge versteckt; und sie sind weder im IE11 noch in den meisten mobilen Geräten verfügbar (und werden es wahrscheinlich nie sein).

Im nächsten Abschnitt sehen wir uns an, wie wir Module in unser Build-Setup integrieren können.

Export

Das Schlüsselwort export ermöglicht es uns, unsere ES6-Module für andere Dateien verfügbar zu machen, und es gibt uns zwei Optionen dafür – benannt und Standard. Mit dem benannten Export können Sie mehrere Exporte pro Modul haben, mit dem Standardexport haben Sie nur einen pro Modul. Benannte Exporte sind besonders nützlich, wenn Sie mehrere Werte exportieren müssen. Zum Beispiel können Sie ein Modul haben, das eine Reihe von Dienstfunktionen enthält, die an verschiedenen Stellen in Ihren Anwendungen verfügbar gemacht werden müssen.

Machen wir also unsere leftPad-Datei zu einem Modul, das wir dann in einer zweiten Datei benötigen.

Benannter Export

Um einen benannten Export zu erstellen, fügen Sie Folgendes am Ende der leftPad-Datei hinzu:

export { leftPad };

Wir können auch die "use strict";-Deklaration am Anfang der Datei entfernen, da Module standardmäßig im Strict-Modus laufen.

Default Export

Da in der leftPad-Datei nur eine einzige Funktion exportiert werden soll, könnte es tatsächlich ein guter Kandidat sein, stattdessen export default zu verwenden:

export default function leftPad(str, len, ch) { ...}

Auch hier kann man die "use strict";-Deklaration vom Anfang der Datei entfernen.

Importieren

Um die exportierten Module zu nutzen, müssen wir sie nun in die Datei (das Modul) importieren, in der wir sie verwenden wollen.

Für die Option export default kann das exportierte Modul unter einem beliebigen Namen importiert werden. Das Modul leftPad kann zum Beispiel so importiert werden:

import leftPad from './leftpad';

Oder es kann unter einem anderen Namen importiert werden, etwa so:

import pineapple_fritter from './leftpad';

Funktional funktionieren beide genau gleich, aber es ist natürlich sinnvoll, entweder den gleichen Namen zu verwenden, unter dem es exportiert wurde, oder etwas, das den Import verständlich macht – vielleicht dort, wo der exportierte Name mit einem anderen Variablennamen kollidieren würde, der bereits im empfangenden Modul existiert.

Beim benannten Export müssen wir das Modul unter demselben Namen importieren, unter dem es exportiert wurde. Für unser Beispielmodul importieren wir es auf ähnliche Weise wie bei der export default-Syntax, aber in diesem Fall müssen wir den importierten Namen mit geschweiften Klammern umschließen:

import { leftPad } from './leftpad';

Die geschweiften Klammern sind bei einem benannten Export obligatorisch, und er schlägt fehl, wenn sie nicht verwendet werden.

Es ist möglich, den Namen eines benannten Exports beim Import zu ändern, wenn dies erforderlich ist, und um dies zu tun, müssen wir unsere Syntax mit einer import as -Syntax ein wenig ändern. Wie bei export gibt es auch hier eine Reihe von Möglichkeiten, die alle auf der MDN-Import-Seite beschrieben sind.

import { leftPad as pineapple_fritter } from './leftpad_es6';

Auch hier ist die Namensänderung etwas unsinnig, aber sie verdeutlicht, dass sie in alles geändert werden können. Man sollte sich immer an gute Namensgebungen halten, es sei denn natürlich, man schreibt Routinen für die Zubereitung von Rezepten auf Obstbasis.

Das exportierte Modul verwenden

Um das exportierte leftPad Modul zu verwenden, habe ich die folgende index.js Datei im Ordner src/js erstellt. Hier durchlaufe ich ein Array von Seriennummern und stelle ihnen Nullen voran, um sie zu einer achtstelligen Zeichenkette zu machen. Später werden wir dies nutzen und sie in einem geordneten Listenelement auf einer HTML-Seite ausgeben. Beachten Sie, dass dieses Beispiel die Standard-Export-Syntax verwendet:

Wie zuvor führen Sie das Build-Skript aus dem Verzeichnis <ROOT> aus:

npm run build

Babel erstellt nun eine index.js-Datei im Verzeichnis public/js. Wie bei unserer leftPad.js-Datei sollten Sie sehen, dass Babel die gesamte ES6-Syntax ersetzt und nur die ES5-Syntax zurückgelassen hat. Vielleicht fällt Ihnen auch auf, dass die ES6-Modulsyntax in die Node-basierte module.exports-Syntax umgewandelt wurde, was bedeutet, dass wir sie von der Befehlszeile aus ausführen können:

node public/js/index.js// 

Ihr Terminal sollte nun ein Array von Strings mit vorangestellten Nullen ausgeben, damit sie alle acht Zeichen lang sind. Nun ist es an der Zeit, einen Blick auf webpack zu werfen.

Einführung in webpack und Integration mit Babel

Wie bereits erwähnt, erlauben ES6-Module dem JavaScript-Entwickler, seinen Code in überschaubare Teile zu zerlegen, was jedoch zur Folge hat, dass diese Teile dem anfragenden Browser zur Verfügung gestellt werden müssen, was möglicherweise Dutzende von zusätzlichen HTTP-Anfragen an den Server nach sich zieht – etwas, das wir eigentlich vermeiden sollten. Hier kommt webpack ins Spiel.

webpack ist ein Modulbündler. Sein Hauptzweck ist es, Ihre Anwendung zu verarbeiten, indem es alle Abhängigkeiten aufspürt und sie dann in ein oder mehrere Pakete verpackt, die im Browser ausgeführt werden können. Es kann jedoch weit mehr als das sein, je nachdem, wie es konfiguriert ist.

Die Konfiguration von Webpack basiert auf vier Schlüsselkomponenten:

  • einem Einstiegspunkt
  • einem Ausgabeort
  • Lader
  • Plugins

Einstieg: Dies enthält den Startpunkt Ihrer Anwendung, von dem aus webpack seine Abhängigkeiten identifizieren kann.

Ausgabe: Hier wird angegeben, wo das verarbeitete Bundle gespeichert werden soll.

Loader: Diese sind eine Möglichkeit, eine Sache als Eingabe zu konvertieren und etwas anderes als Ausgabe zu erzeugen. Sie können verwendet werden, um die Fähigkeiten von webpack zu erweitern, um mehr als nur JavaScript-Dateien zu verarbeiten, und daher auch diese in gültige Module umzuwandeln.

Plugins: Diese werden verwendet, um die Fähigkeiten von webpack um andere Aufgaben zu erweitern, die über das Bündeln hinausgehen – wie z.B. Minification, Linting und Optimierung.

Um webpack zu installieren, führen Sie das Folgende aus Ihrem <ROOT>-Verzeichnis aus:

npm install webpack webpack-cli --save-dev

Dies installiert webpack lokal im Projekt und gibt Ihnen auch die Möglichkeit, webpack von der Kommandozeile aus durch das Hinzufügen von webpack-cli zu starten. Sie sollten nun webpack in Ihrer package.json-Datei sehen. Während Sie in dieser Datei sind, ändern Sie den Abschnitt scripts wie folgt, so dass es nun weiß, dass es webpack anstelle von Babel direkt verwendet:

"scripts": { "build": "webpack --config webpack.config.js"},

Wie Sie sehen können, ruft dieses Skript eine webpack.config.js Datei auf, also lassen Sie uns diese in unserem <ROOT> Verzeichnis mit dem folgenden Inhalt erstellen:

Dies ist mehr oder weniger die einfachste Konfigurationsdatei, die Sie mit webpack benötigen. Sie können sehen, dass sie die zuvor beschriebenen Eingabe- und Ausgabeabschnitte verwendet (mit diesen allein könnte sie funktionieren), aber auch eine mode: 'development'-Einstellung enthält.

webpack hat die Möglichkeit, entweder den „Entwicklungs-“ oder den „Produktionsmodus“ zu verwenden. Die Einstellung mode: 'development' optimiert die Geschwindigkeit beim Erstellen und Debuggen, während mode: 'production' die Ausführungsgeschwindigkeit zur Laufzeit und die Größe der Ausgabedatei optimiert. Es gibt eine gute Erklärung der Modi in Tobias Koppers‘ Artikel „webpack 4: mode and optimization“, wenn Sie mehr darüber lesen möchten, wie sie über die Standardeinstellungen hinaus konfiguriert werden können.

Nächste entfernen Sie alle Dateien aus dem Ordner public/js. Führen Sie dann diesen Befehl erneut aus:

npm run build

Sie werden sehen, dass er jetzt eine einzige ./public/bundle.js-Datei enthält. Wenn Sie die neue Datei öffnen, sehen die beiden Dateien, mit denen wir begonnen haben, allerdings etwas anders aus. Dies ist der Abschnitt der Datei, der den index.js-Code enthält. Obwohl er gegenüber dem Original stark verändert wurde, kann man die Variablennamen noch erkennen:

Wenn man node public/js/bundle.js aus dem Ordner <ROOT> ausführt, erhält man die gleichen Ergebnisse wie zuvor.

Transpilieren

Wie bereits erwähnt, können wir mit Loadern eine Sache in etwas anderes umwandeln. In diesem Fall wollen wir ES6 in ES5 umwandeln. Dazu brauchen wir ein paar weitere Pakete:

npm install babel-loader babel-core --save-dev

Um sie zu nutzen, muss dem webpack.config.js ein Modulabschnitt nach dem Ausgabeabschnitt hinzugefügt werden, etwa so:

Dies verwendet eine Regex-Anweisung, um die JavaScript-Dateien zu identifizieren, die mit dem babel-loader transpiliert werden sollen, während alles im Ordner node_modules davon ausgeschlossen wird. Schließlich wird babel-loader angewiesen, das zuvor installierte babel-preset-env-Paket zu verwenden, um die in der .babelrc-Datei festgelegten Transpile-Parameter festzulegen.

Nachdem dies geschehen ist, können Sie das Programm erneut ausführen:

npm run build

Überprüfen Sie dann das neue public/js/bundle.js und Sie werden sehen, dass alle Spuren der ES6-Syntax verschwunden sind, aber es erzeugt immer noch die gleiche Ausgabe wie zuvor.

In den Browser bringen

Nachdem wir ein funktionierendes Webpack- und Babel-Setup gebaut haben, ist es an der Zeit, das, was wir gemacht haben, in den Browser zu bringen. Es wird eine kleine HTML-Datei benötigt, die im Ordner <ROOT> wie folgt erstellt werden sollte:

Ein wenig mehr JavaScript wird benötigt, um die Liste anzuzeigen, also ändern wir ./src/js/index.js, um dies zu ermöglichen:

Wenn Sie nun index.html in Ihrem Browser öffnen, sollten Sie eine geordnete Liste sehen, etwa so:

Weiter geht’s

Wie oben konfiguriert, ist unser Build-System so ziemlich einsatzbereit. Wir können nun webpack verwenden, um unsere Module zu bündeln und ES6-Code mit Babel nach ES5 zu transpilieren.

Es ist jedoch etwas ärgerlich, dass wir für die Transpilierung unseres ES6-Codes jedes Mal npm run build ausführen müssen, wenn wir eine Änderung vornehmen.

Hinzufügen einer ‚watch‘

Um die Notwendigkeit zu umgehen, npm run build wiederholt auszuführen, können Sie eine 'watch' auf Ihre Dateien setzen und webpack automatisch neu kompilieren lassen, wenn es eine Änderung in einer der Dateien im ./src Ordner sieht. Um dies zu implementieren, ändern Sie den scripts-Abschnitt der package.json-Datei wie folgt:

"scripts": { "watch": "webpack --watch", "build": "webpack --config webpack.config.js"},

Um zu überprüfen, ob es funktioniert, führen Sie npm run watch im Terminal aus, und Sie werden sehen, dass es nicht mehr zur Eingabeaufforderung zurückkehrt. Gehen Sie nun zurück zu src/js/index.js und fügen Sie einen zusätzlichen Wert in das Array serNos ein und speichern Sie es. Meines sieht jetzt so aus:

const serNos = ;

Wenn Sie jetzt das Terminal überprüfen, werden Sie sehen, dass es abgemeldet ist und die Aufgabe webpack build erneut ausgeführt hat. Wenn Sie nun zum Browser zurückkehren und die Seite aktualisieren, sehen Sie, dass der neue Wert am Ende der Liste hinzugefügt wurde, nachdem er mit leftPad verarbeitet wurde.

Den Browser automatisch aktualisieren

Es wäre jetzt wirklich gut, wenn wir webpack dazu bringen könnten, den Browser automatisch zu aktualisieren, wenn wir eine Änderung vornehmen. Dazu installieren wir ein zusätzliches npm-Paket namens webpack-dev-server. Vergessen Sie aber nicht, die Aufgabe watch mit Strg + c zu beenden!

npm install webpack-dev-server --save-dev

Nachdem das erledigt ist, fügen wir ein neues Skript in die Datei package.json ein, um das neue Paket aufzurufen. Der scripts-Abschnitt sollte nun folgendes enthalten:

Beachten Sie das --open-page-Flag, das am Ende des Skripts hinzugefügt wurde. Dadurch wird webpack-dev-server angewiesen, eine bestimmte Seite in Ihrem Standardbrowser im iframe-Modus zu öffnen.

Führen Sie nun npm start aus, und Sie sollten sehen, dass unter eine neue Browser-Registerkarte geöffnet wird, in der die Teileliste angezeigt wird. Um zu zeigen, dass 'watch' funktioniert, gehen Sie zu src/js/index.js und fügen Sie einen weiteren neuen Wert am Ende des Arrays serNos hinzu. Wenn du deine Änderungen speicherst, sollten sie sich fast sofort im Browser widerspiegeln.

Damit ist das Einzige, was noch fehlt, dass der Modus in webpack.config.js auf production gesetzt wird. Sobald dies geschehen ist, wird webpack den Code, den es ausgibt, auch in ./public/js/bundle.js minifizieren. Sie sollten beachten, dass, wenn mode nicht gesetzt ist, webpack standardmäßig die production-Konfiguration verwendet.

Abschluss

In diesem Artikel haben Sie gesehen, wie man ein Build-System für modernes JavaScript einrichtet. Zunächst wurde Babel von der Kommandozeile aus verwendet, um ES6-Syntax in ES5 zu konvertieren. Sie haben dann gesehen, wie man ES6-Module mit den Schlüsselwörtern export und import verwendet, wie man webpack integriert, um eine Bündelungsaufgabe durchzuführen, und wie man eine Überwachungsaufgabe hinzufügt, um die Ausführung von webpack zu automatisieren, sobald Änderungen an einer Quelldatei erkannt werden. Schließlich haben Sie gesehen, wie man webpack-dev-server installiert, um die Seite bei jeder Änderung automatisch zu aktualisieren.

Sollten Sie dies weiter vertiefen wollen, empfehle ich Ihnen, SitePoints Deep Dive in Webpack und Modulbündelung zu lesen sowie zusätzliche Loader und Plugins zu erforschen, die es Webpack ermöglichen, Sass- und Asset-Komprimierungsaufgaben zu erledigen. Schauen Sie sich auch die eslint-loader und das Plugin für Prettier an.

Happy bundling …