Warum npm-Skripte?

Avatar of Damon Bauer
Damon Bauer am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es gibt eine wachsende Meinung (z. B. zum Beispiel), dass die direkte Verwendung von Node-Paketen mit den von ihnen bereitgestellten Kommandozeilenbefehlen ein guter Weg ist. Im Gegensatz zur Abstraktion der Funktionalität hinter einem Task-Runner. Teilweise: Man benutzt sowieso npm, npm bietet Skript-Funktionalität, warum nicht einfach diese nutzen? Aber es steckt mehr dahinter. Lassen Sie uns die Überlegungen durchgehen, aber auch genau, wie man viele der wichtigsten Aufgaben in einem Frontend-Entwicklungs-Build-Prozess erledigt.

Ich verwende jetzt seit etwa sechs Monaten npm-Skripte in meinen Projekten. Davor habe ich Gulp und davor Grunt verwendet. Sie haben mir gute Dienste geleistet und mir geholfen, meine Arbeit schneller und effizienter zu erledigen, indem sie viele Dinge automatisierten, die ich früher von Hand erledigte. Ich begann jedoch das Gefühl zu haben, dass ich gegen die Werkzeuge kämpfte, anstatt mich auf meinen eigenen Code zu konzentrieren.

Grunt, Gulp, Broccoli, Brunch und ähnliche Tools erfordern alle, dass man seine Aufgaben an ihre Paradigmen und Konfigurationen anpasst. Jedes hat seine eigenen Syntaxen, Eigenheiten und Tücken, die man lernen muss. Dies erhöht die Codekomplexität, die Build-Komplexität und zwingt einen, sich auf die Behebung von Werkzeugen zu konzentrieren, anstatt Code zu schreiben.

Diese Build-Tools sind auf Plugins angewiesen, die ein Kern-Kommandozeilen-Tool umschließen. Dies schafft eine weitere Abstraktionsebene vom Kern-Tool, was mehr Potenzial für Probleme bedeutet.

Hier sind drei Probleme, die ich mehrmals gesehen habe

  • Wenn es kein Plugin für das Kommandozeilen-Tool gibt, das Sie verwenden möchten, haben Sie Pech (es sei denn, Sie schreiben es selbst).
  • Ein Plugin, das Sie zu verwenden versuchen, umschließt eine ältere Version des Tools, das Sie verwenden möchten. Features und Dokumentation stimmen nicht immer zwischen dem von Ihnen verwendeten Plugin und der aktuellen Version des Kern-Tools überein.
  • Fehler werden nicht immer gut behandelt. Wenn ein Plugin fehlschlägt, gibt es möglicherweise nicht die Fehlermeldung des Kern-Tools weiter, was zu Frustration führt und man weiß nicht wirklich, wie man das Problem debuggt.

Aber, bedenken Sie…

Lassen Sie mich das sagen: Wenn Sie mit Ihrem aktuellen Build-System zufrieden sind und es alles leistet, was Sie benötigen, können Sie es weiterhin verwenden! Nur weil npm-Skripte beliebter werden, heißt das nicht, dass Sie das Schiff wechseln sollten. Konzentrieren Sie sich weiterhin auf das Schreiben Ihres Codes, anstatt mehr Werkzeuge zu lernen. Wenn Sie das Gefühl bekommen, dass Sie mit Ihren Werkzeugen kämpfen, dann würde ich in Erwägung ziehen, npm-Skripte zu verwenden.

Wenn Sie sich entschieden haben, npm-Skripte zu untersuchen oder zu verwenden, lesen Sie weiter! Im Rest dieses Beitrags finden Sie viele Beispielaufgaben. Außerdem habe ich npm-build-boilerplate mit all diesen Aufgaben erstellt, das Sie als Ausgangspunkt verwenden können. Los geht's!

npm-Skripte schreiben

Den Großteil unserer Zeit werden wir in einer `package.json`-Datei verbringen. Hier leben alle unsere Abhängigkeiten und Skripte. Hier ist eine abgespeckte Version aus meinem Boilerplate-Projekt

{
  "name": "npm-build-boilerplate",
  "version": "1.0.0",
  "scripts": {
    ...
  },
  "devDependencies": {
    ...
  }
}

Wir werden unsere `package.json`-Datei im Laufe der Zeit aufbauen. Unsere Skripte kommen in das `scripts`-Objekt, und alle Werkzeuge, die wir verwenden möchten, werden installiert und in das `devDependencies`-Objekt aufgenommen.

Bevor wir beginnen, hier ist eine Beispielstruktur des Projekts, auf das ich im gesamten Beitrag Bezug nehmen werde

SCSS zu CSS kompilieren

Ich bin ein starker Nutzer von SCSS, daher werde ich damit arbeiten. Um SCSS zu CSS zu kompilieren, wende ich mich an node-sass. Zuerst müssen wir `node-sass` installieren; tun Sie dies, indem Sie Folgendes in Ihrer Kommandozeile ausführen

npm install --save-dev node-sass

Dies installiert `node-sass` in Ihrem aktuellen Verzeichnis und fügt es dem `devDependencies`-Objekt in Ihrer `package.json` hinzu. Dies ist besonders nützlich, wenn jemand anderes Ihr Projekt ausführt, da er alles hat, was er braucht, um das Projekt zum Laufen zu bringen. Nach der Installation können wir es auf der Kommandozeile verwenden

node-sass --output-style compressed -o dist/css src/scss

Lassen Sie uns aufschlüsseln, was dieser Befehl tut. Beginnend am Ende sagt er: Schau im `src/scss`-Ordner nach allen SCSS-Dateien; gib die kompilierten CSS-Dateien im Ordner `dist/css` aus (Flag `-o`); komprimiere die Ausgabe (mit dem Flag `--output-style` mit "compressed" als Option).

Jetzt, da wir das auf der Kommandozeile zum Laufen gebracht haben, verschieben wir es in ein npm-Skript. Fügen Sie es in Ihrem `scripts`-Objekt von `package.json` wie folgt hinzu

"scripts": {
  "scss": "node-sass --output-style compressed -o dist/css src/scss"
}

Gehen Sie nun zurück zur Kommandozeile und führen Sie

npm run scss

Sie sehen dieselbe Ausgabe wie bei der direkten Ausführung des `node-sass`-Befehls in der Kommandozeile.

Jedes Mal, wenn wir in diesem Beitrag ein npm-Skript erstellen, können Sie es mit einem Befehl wie dem obigen ausführen.

Ersetzen Sie einfach `scss` durch den Namen der Aufgabe, die Sie ausführen möchten.

Wie Sie sehen werden, haben viele der Kommandozeilen-Tools, die wir verwenden werden, zahlreiche Optionen, mit denen Sie sie genau nach Ihren Wünschen konfigurieren können. Zum Beispiel hier die Liste der (node-sass Optionen). Hier ist eine andere Konfiguration, die zeigt, wie mehrere Optionen übergeben werden können

"scripts": {
  "scss": "node-sass --output-style nested --indent-type tab --indent-width 4 -o dist/css src/scss"
}

CSS mit PostCSS autoprefixen

Da wir jetzt SCSS zu CSS kompilieren, können wir mit Autoprefixer & PostCSS automatisch Vendor-Präfixe hinzufügen. Wir können mehrere Module gleichzeitig installieren, getrennt durch Leerzeichen

npm install --save-dev postcss-cli autoprefixer

Wir installieren zwei Module, da PostCSS standardmäßig nichts tut. Es stützt sich auf andere Plugins wie Autoprefixer, um das von Ihnen bereitgestellte CSS zu manipulieren.

Nachdem die notwendigen Werkzeuge installiert und in `devDependencies` gespeichert wurden, fügen Sie eine neue Aufgabe in Ihrem `scripts`-Objekt hinzu

"scripts": {
  ...
  "autoprefixer": "postcss -u autoprefixer -r dist/css/*"
}

Diese Aufgabe besagt: Hey `postcss`, verwende (Flag `-u`) `autoprefixer`, um alle `.css`-Dateien in `dist/css` mit Vendor-Prefixed-Code zu ersetzen (Flag `-r`). Das war's! Müssen Sie die Standardbrowserunterstützung für Autoprefixer ändern? Es ist einfach, es zum Skript hinzuzufügen

"autoprefixer": "postcss -u autoprefixer --autoprefixer.browsers '> 5%, ie 9' -r dist/css/*"

Auch hier gibt es viele Optionen, die Sie zur Konfiguration Ihrer eigenen Builds verwenden können: postcss-cli und autoprefixer.

JavaScript-Linting

Die Einhaltung eines standardisierten Formats und Stils beim Schreiben von Code ist wichtig, um Fehler zu minimieren und die Entwicklereffizienz zu steigern. "Linting" hilft uns dabei automatisch, also fügen wir JavaScript-Linting hinzu, indem wir eslint verwenden.

Installieren Sie das Paket noch einmal; diesmal verwenden wir eine Abkürzung

npm i -D eslint

Dies ist dasselbe wie

npm install --save-dev eslint

Nach der Installation richten wir einige grundlegende Regeln ein, gegen die unser Code geprüft wird, mit `eslint`. Führen Sie Folgendes aus, um einen Assistenten zu starten

eslint --init

Ich schlage vor, "Fragen zu Ihrem Stil beantworten" zu wählen und die gestellten Fragen zu beantworten. Dies erstellt eine neue Datei im Stammverzeichnis Ihres Projekts, gegen die `eslint` Ihren Code prüft.

Nun fügen wir eine Lint-Aufgabe zu unserem `scripts`-Objekt in `package.json` hinzu

"scripts": {
  ...
  "lint": "eslint src/js"
}

Unsere Lint-Aufgabe ist 13 Zeichen lang! Sie sucht nach allen JavaScript-Dateien im Ordner `src/js` und führt sie gegen die zuvor erstellte Konfiguration aus. Natürlich können Sie mit den Optionen verrückt werden.

JavaScript-Dateien verkleinern

Lassen Sie uns daran arbeiten, unsere JavaScript-Dateien zu kombinieren und zu minifizieren, wozu wir uglify-js verwenden können. Wir müssen zunächst `uglify-js` installieren

npm i -D uglify-js

Dann können wir unsere Uglify-Aufgabe in `package.json` einrichten

"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js"
}

Eines der großartigen Dinge an npm-Skripten ist, dass sie im Wesentlichen ein Alias für eine Kommandozeilenaufgabe sind, die Sie immer wieder ausführen möchten. Das bedeutet, dass Sie Standard-Kommandozeilen-Code direkt in Ihrem Skript verwenden können! Diese Aufgabe verwendet zwei Standard-Kommandozeilenfunktionen: `mkdir` und `&&`.

Der erste Teil dieser Aufgabe, `mkdir -p dist/js`, besagt: Erstelle eine Ordnerstruktur (`mkdir`), aber nur, wenn sie noch nicht existiert (`-p`-Flag). Sobald das erfolgreich abgeschlossen ist, führe den `uglifyjs`-Befehl aus. Das `&&` ermöglicht es Ihnen, mehrere Befehle zu verketten und jeden nacheinander auszuführen, *wenn* der vorherige Befehl erfolgreich abgeschlossen wurde.

Der zweite Teil dieser Aufgabe weist `uglifyjs` an, mit allen JS-Dateien (`*.js`) in `src/js/` zu beginnen, den "mangle"-Befehl anzuwenden (Flag `-m`) und das Ergebnis nach `dist/js/app.js` auszugeben. Überprüfen Sie noch einmal die Dokumentation des betreffenden Tools für eine vollständige Liste von Optionen.

Aktualisieren wir unsere `uglify`-Aufgabe, um eine komprimierte Version von `dist/js/app.js` zu erstellen. Verkette einen weiteren `uglifyjs`-Befehl und übergib das Flag "compress" (`-c`)

"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js && uglifyjs src/js/*.js -m -c -o dist/js/app.min.js"
}

Bilder komprimieren

Wenden wir uns nun der Komprimierung von Bildern zu. Laut httparchive.org beträgt das durchschnittliche Seiten-Gewicht der Top-1000-URLs im Internet 1,9 MB, wobei Bilder 1,1 MB dieses Gesamtbetrags ausmachen. Eines der besten Dinge, das Sie zur Steigerung der Seitengeschwindigkeit tun können, ist die Reduzierung der Größe Ihrer Bilder.

Installieren Sie imagemin-cli

npm i -D imagemin-cli

Imagemin ist großartig, da es die meisten Bildtypen, einschließlich GIF, JPG, PNG und SVG, komprimiert. Sie können ihm einen Ordner mit Bildern übergeben, und er wird sie alle bearbeiten, wie hier

"scripts": {
  ...
  "imagemin": "imagemin src/images dist/images -p",
}

Diese Aufgabe weist `imagemin` an, alle Bilder in `src/images` zu finden und zu komprimieren und sie in `dist/images` abzulegen. Das Flag `-p` wird übergeben, um "progressive" Bilder zu erstellen, wo immer möglich. Überprüfen Sie die Dokumentation für alle verfügbaren Optionen.

SVG-Sprites

Die Begeisterung für SVG ist in den letzten Jahren gestiegen, und das aus gutem Grund. Sie sind gestochen scharf auf allen Geräten, mit CSS editierbar und bildschirmleserfreundlich. Allerdings hinterlassen SVG-Bearbeitungssoftware normalerweise überflüssige und unnötige Codes. Glücklicherweise kann svgo helfen, all das zu entfernen (wir werden es unten installieren).

Sie können auch den Prozess des Kombinierens und Spriting Ihrer SVGs automatisieren, um eine einzelne SVG-Datei zu erstellen (mehr über diese Technik hier). Um diesen Prozess zu automatisieren, können wir svg-sprite-generator installieren.

npm i -D svgo svg-sprite-generator

Das Muster ist Ihnen wahrscheinlich mittlerweile vertraut: Nach der Installation fügen Sie eine Aufgabe in Ihrem `scripts`-Objekt von `package.json` hinzu

"scripts": {
  ...
  "icons": "svgo -f src/images/icons && mkdir -p dist/images && svg-sprite-generate -d src/images/icons -o dist/images/icons.svg"
}

Beachten Sie, dass die `icons`-Aufgabe drei Dinge tut, basierend auf der Anwesenheit von zwei `&&`-Direktiven. Erstens verwenden wir `svgo`, übergeben einen Ordner (Flag `-f`) von SVGs; dies komprimiert alle SVGs im Ordner. Zweitens erstellen wir den Ordner `dist/images`, falls er noch nicht existiert (mit dem Befehl `mkdir -p`). Drittens verwenden wir `svg-sprite-generator`, übergeben ihm einen Ordner von SVGs (Flag `-d`) und einen Pfad, wohin wir den SVG-Sprite ausgeben möchten (Flag `-o`).

Server starten und Änderungen automatisch mit BrowserSync injizieren

Eines der letzten Puzzleteile ist BrowserSync. Einige Dinge, die es tun kann: einen lokalen Server starten, aktualisierte Dateien automatisch in jeden verbundenen Browser injizieren und Klicks & Scrollen zwischen Browsern synchronisieren. Installieren Sie es und fügen Sie eine Aufgabe hinzu

npm i -D browser-sync
"scripts": {
  ...
  "serve": "browser-sync start --server --files 'dist/css/*.css, dist/js/*.js'"
}

Unsere BrowserSync-Aufgabe startet einen Server (Flag `--server`), der den aktuellen Pfad standardmäßig als Root verwendet. Das Flag `--files` weist BrowserSync an, nach CSS- oder JS-Dateien im Ordner `dist` zu suchen. Immer wenn sich etwas darin ändert, werden die geänderten Dateien automatisch in die Seite injiziert.

Sie können mehrere Browser (sogar auf verschiedenen Geräten) öffnen, und alle erhalten in Echtzeit aktualisierte Dateianpassungen!

Aufgaben gruppieren

Mit all den Aufgaben von oben können wir

  • SCSS zu CSS kompilieren und automatisch Vendor-Präfixe hinzufügen
  • JavaScript linten und verkleinern
  • Bilder komprimieren
  • Einen Ordner mit SVGs in einen einzelnen SVG-Sprite konvertieren
  • Einen lokalen Server starten und Änderungen automatisch in jeden mit dem Server verbundenen Browser injizieren

Hören wir nicht auf!

CSS-Aufgaben kombinieren

Fügen wir eine Aufgabe hinzu, die die beiden CSS-bezogenen Aufgaben (Sass-Preprocessing und Ausführung von Autoprefixer) kombiniert, damit wir sie nicht einzeln ausführen müssen

"scripts": {
  ...
  "build:css": "npm run scss && npm run autoprefixer"
}

Wenn Sie `npm run build:css` ausführen, wird die Kommandozeile angewiesen, `npm run scss` auszuführen; wenn dies erfolgreich abgeschlossen ist, wird dann (`&&`) `npm run autoprefixer` ausgeführt.

Genau wie bei unserer `build:css`-Aufgabe können wir unsere JavaScript-Aufgaben verketten, um die Ausführung zu erleichtern

JavaScript-Aufgaben kombinieren

"scripts": {
  ...
  "build:js": "npm run lint && npm run uglify"
}

Jetzt können wir `npm run build:js` aufrufen, um unser JavaScript in einem Schritt zu linten, zu verketten und zu verkleinern!

Verbleibende Aufgaben kombinieren

Das Gleiche können wir für unsere Bildaufgaben tun, sowie eine Aufgabe, die alle Build-Aufgaben in einer zusammenfasst

"scripts": {
  ...
  "build:images": "npm run imagemin && npm run icons",
  "build:all": "npm run build:css && npm run build:js && npm run build:images",
}

Auf Änderungen warten

Bis zu diesem Punkt erfordern unsere Aufgaben, dass wir Änderungen an einer Datei vornehmen, zurück zum Kommandozeilenfenster wechseln und die entsprechenden Aufgaben ausführen. Eines der nützlichsten Dinge, die wir tun können, ist das Hinzufügen von Aufgaben, die auf Änderungen warten und Aufgaben automatisch ausführen, wenn Dateien geändert werden. Um dies zu tun, empfehle ich die Verwendung von onchange. Installieren wie üblich

npm i -D onchange

Lassen Sie uns Watch-Aufgaben für CSS und JavaScript einrichten

"scripts": {
  ...
  "watch:css": "onchange 'src/scss/*.scss' -- npm run build:css",
  "watch:js": "onchange 'src/js/*.js' -- npm run build:js",
}

Hier ist die Aufschlüsselung dieser Aufgaben: `onchange` erwartet, dass Sie einen Pfad als Zeichenkette für die Dateien übergeben, die Sie überwachen möchten. Wir werden unsere Quell-SCSS- und JS-Dateien zum Überwachen übergeben. Der Befehl, den wir ausführen möchten, kommt nach den `--`, und er wird jedes Mal ausgeführt, wenn eine Datei im angegebenen Pfad hinzugefügt, geändert oder gelöscht wird.

Fügen wir noch einen Watch-Befehl hinzu, um unseren npm-Skripte-Build-Prozess abzuschließen.

Installieren Sie noch ein Paket, parallelshell

npm i -D parallelshell

Fügen Sie erneut eine neue Aufgabe zum `scripts`-Objekt hinzu

"scripts": {
  ...
  "watch:all": "parallelshell 'npm run serve' 'npm run watch:css' 'npm run watch:js'"
}

`parallelshell` nimmt mehrere Zeichenketten entgegen, denen wir mehrere `npm run`-Aufgaben übergeben.

Warum `parallelshell` verwenden, um mehrere Aufgaben zu kombinieren, anstatt `&&` wie in früheren Aufgaben zu verwenden? Zuerst habe ich das versucht. Das Problem ist, dass `&&` Befehle verkettet und darauf wartet, dass jeder Befehl *erfolgreich abgeschlossen* wird, bevor der nächste gestartet wird. Da wir jedoch `watch`-Befehle ausführen, werden diese niemals beendet! Wir würden in einer Endlosschleife stecken bleiben.

Daher ermöglicht uns die Verwendung von `parallelshell`, mehrere `watch`-Befehle gleichzeitig auszuführen.

Diese Aufgabe startet einen Server mit BrowserSync über die Aufgabe `npm run serve`. Dann startet sie unsere Watch-Aufgaben für CSS- und JavaScript-Dateien. Jedes Mal, wenn eine CSS- oder JavaScript-Datei geändert wird, führt die Watch-Aufgabe eine entsprechende Build-Aufgabe aus; da BrowserSync so konfiguriert ist, dass es auf Änderungen im `dist`-Ordner achtet, injiziert es automatisch neue Dateien in jeden Browser, der mit seiner URL verbunden ist. Super!

Weitere nützliche Aufgaben

`npm` kommt mit vielen integrierten Aufgaben, in die Sie eingreifen können. Lassen Sie uns noch eine Aufgabe schreiben, die eine dieser integrierten Skripte nutzt.

"scripts": {
  ...
  "postinstall": "npm run watch:all"
}

`postinstall` wird sofort ausgeführt, nachdem Sie `npm install` in Ihrer Kommandozeile ausgeführt haben. Dies ist ein Nice-to-have, besonders wenn Sie in Teams arbeiten. Wenn jemand Ihr Projekt klont und `npm install` ausführt, startet sofort unsere `watch:all`-Aufgabe. Sie haben automatisch einen Server gestartet, ein Browserfenster geöffnet und die Dateien werden auf Änderungen überwacht.

Zusammenfassung

Puh! Wir haben es geschafft! Ich hoffe, Sie konnten ein paar Dinge über die Verwendung von npm-Skripten als Build-Prozess und die Kommandozeile im Allgemeinen lernen.

Falls Sie es verpasst haben, habe ich ein npm-build-boilerplate-Projekt mit all diesen Aufgaben erstellt, das Sie als Ausgangspunkt verwenden können. Wenn Sie Fragen oder Kommentare haben, twittern Sie mich an oder hinterlassen Sie unten einen Kommentar. Ich helfe Ihnen gerne weiter!