Gulp für WordPress: Ersteinrichtung

Avatar of Ali Alaa
Ali Alaa am

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

Dies ist der erste Teil einer zweiteiligen Serie zur Erstellung eines Gulp-Workflows für die WordPress-Theme-Entwicklung. Dieser erste Teil behandelt viel Stoff für die Ersteinrichtung, einschließlich der Gulp-Installation und einer Übersicht über die Aufgaben, die wir von Gulp ausführen lassen möchten. Wenn Sie daran interessiert sind, wie die Aufgaben erstellt werden, dann bleiben Sie dran für Teil zwei.

Anfang dieses Jahres habe ich einen Kurs zum Erstellen von Premium-WordPress-Themes erstellt. Während des Prozesses wollte ich einen Task Runner verwenden, um JavaScript- und CSS-Dateien zu verketten und zu minimieren. Ich habe diesen Task Runner letztendlich verwendet, um viele andere Aufgaben zu automatisieren, die das Theme effizienter und skalierbarer machten.

Die beiden beliebtesten Task Runner, die von Node angetrieben werden, sind Gulp und Grunt. Ich habe mich nach einiger Recherche für Gulp entschieden, da es mir eine intuitive Möglichkeit zur Erstellung von Aufgaben zu bieten schien. Es verwendet Node-Streams, um Dateien zu manipulieren, und JavaScript-Funktionen, um die Aufgaben zu schreiben, während Grunt ein Konfigurationsobjekt zur Definition von Aufgaben verwendet – was für manche in Ordnung sein mag, aber etwas, das mich ein wenig beunruhigt hat. Außerdem ist Gulp dank dieser Node-Streams etwas schneller als Grunt, und schneller ist für mich immer gut!

Wir werden also Gulp so einrichten, dass es viel von der Schwerstarbeit für die WordPress-Theme-Entwicklung übernimmt. Wir werden uns vorerst mit der Ersteinrichtung befassen, aber dann in einem anderen Beitrag tief in die Aufgaben selbst eintauchen.

Artikelserie

  1. Ersteinrichtung (Dieser Beitrag)
  2. Erstellung der Aufgaben

Ersteinrichtung des Themes

Wie können wir Gulp also für die Aufgaben eines WordPress-Themes nutzen? Zunächst gehen wir davon aus, dass unser Theme nur die beiden Dateien enthält, die WordPress für jedes Theme benötigt: index.php und styles.css. Sicher, die meisten Themes enthalten wahrscheinlich viele weitere Dateien, aber das ist im Moment nicht wichtig.

Zweitens gehen wir davon aus, dass unser Hauptziel darin besteht, Aufgaben zu erstellen, die uns bei der Verwaltung unserer Assets helfen, wie z. B. das Minimieren unserer CSS- und JavaScript-Dateien, das Kompilieren von Sass zu CSS und das Transpilieren moderner JavaScript-Syntax (z. B. ES6, ES7 usw.) in ES5, um ältere Browser zu unterstützen.

Unsere Theme-Ordnerstruktur wird so aussehen

themefolder/
├── index.php
├── style.css
└── src/
    ├── images/
    │   └── cat.jpg
    ├── js/
    │   ├── components/
    │   │   └── slider.js
    │   └── bundle.js
    └── scss/
        ├── components/
        │   └── slider.scss
        └── bundle.scss

Das Einzige, was wir zu den beiden erforderlichen Dateien hinzugefügt haben, ist ein src-Verzeichnis, in dem sich unsere ursprünglichen, unkompilierten Assets befinden.

Innerhalb dieses src-Verzeichnisses haben wir ein Unterverzeichnis images sowie weitere für unsere JavaScript- und Sass-Dateien. Von dort aus sind die Unterverzeichnisse für JavaScript und Sass in Komponenten organisiert, die von ihrem jeweiligen bundle-File aufgerufen werden. Zum Beispiel wird bundle.js slider.js importieren und einschließen, wenn unsere JavaScript-Aufgaben ausgeführt werden, sodass unser gesamter Code in eine einzige Datei verkettet wird.

Gulp-Aufgaben identifizieren

Okay, als Nächstes möchten wir, dass Gulp-Aufgaben ein neues dist-Verzeichnis erstellen, in dem alle kompilierten, minimierten und verketteten Versionen unserer Assets nach Abschluss der Aufgaben verteilt werden. Obwohl wir dieses Verzeichnis in diesem Beitrag dist nennen, weil es für "Distribution" steht, könnte es wirklich beliebig genannt werden, solange Gulp weiß, wie es heißt. Unabhängig davon handelt es sich um die Assets, die an Endbenutzer verteilt werden. Der src-Ordner enthält nur die Dateien, die wir während der Entwicklung direkt bearbeiten.

Die Identifizierung der besten Gulp-Aufgaben für ein Projekt hängt von den spezifischen Bedürfnissen des Projekts ab. Einige Aufgaben sind für einige Projekte sehr hilfreich, für andere aber völlig irrelevant. Ich habe die folgenden für diesen Beitrag identifiziert. Sie werden feststellen, dass ein oder zwei im WordPress-Kontext nützlicher sind (z. B. die POT-Aufgabe) als andere. Die meisten dieser Aufgaben sind jedoch breit genug gefasst, dass Sie sie wahrscheinlich in vielen Projekten finden, die Gulp zur Verarbeitung von Sass, JavaScript und Bild-Assets verwenden.

  • Styles-Aufgabe: Diese Aufgabe ist dafür verantwortlich, die Datei bundle.scss im Unterverzeichnis scss nach bundle.css in einem css-Verzeichnis im dist-Verzeichnis zu kompilieren. Diese Aufgabe minimiert auch die generierte CSS-Datei, damit sie in der Produktion ihre kleinstmögliche Größe hat.

Wir werden während des Artikels über Produktions- und Entwicklungsmodi sprechen. Beachten Sie, dass wir keine Aufgabe zum Verketten von CSS-Dateien erstellen werden. Die Datei bundle.scss dient als Einstiegspunkt für alle .scss-Dateien, die wir einbeziehen möchten. Mit anderen Worten; alle Sass- oder CSS-Dateien, die Sie in Ihr Projekt einbeziehen möchten, importieren Sie einfach in die Datei bundle.scss mithilfe von @import-Anweisungen. Zum Beispiel können wir in unserem Beispielordner @import ./components/slider'; verwenden, um die Datei slider.scss zu importieren. Auf diese Weise müssen wir in unserer Aufgabe nur eine Datei (bundle.css) kompilieren und minimieren.

  • Scripts-Aufgabe: Ähnlich wie die Styles-Aufgabe transpilert diese Aufgabe bundle.js von ES6-Syntax nach ES5 und minimiert die Datei dann für die Produktion.

Wir werden nur bundle.js kompilieren. Alle anderen JavaScript-Dateien, die wir einbeziehen möchten, werden über ES6 import statements erfolgen. Aber damit diese Import-Anweisungen in allen Browsern funktionieren, benötigen wir einen Modul-Bundler. Wir werden webpack als unseren Bundler verwenden. Wenn Sie zum ersten Mal damit arbeiten, ist dieser Leitfaden ein guter Ort, um einen Überblick darüber zu bekommen, was es ist und was es tut.

  • Images-Aufgabe: Diese Aufgabe kopiert einfach Bilder aus src/images und leitet sie nach dist/images weiter, nachdem die Dateien auf ihre kleinste Größe komprimiert wurden.
  • Copy-Aufgabe: Diese Aufgabe ist dafür verantwortlich, alle anderen Dateien oder Ordner zu kopieren, die sich nicht in /src/images, /src/js oder /src/scss befinden, und sie in das dist-Verzeichnis zu übertragen.

Denken Sie daran. Der src-Ordner enthält die Dateien, die nur während der Entwicklung verwendet werden und nicht im endgültigen Theme-Paket enthalten sind. Daher müssen alle Assets außer unseren Bild-, JavaScript- und Sass-Dateien in den dist-Ordner kopiert und dort abgelegt werden. Wenn wir beispielsweise einen /src/fonts-Ordner hätten, würden wir die Dateien dort in das dist-Verzeichnis kopieren wollen, damit sie in der endgültigen Lieferung enthalten sind.

  • POT-Aufgabe: Wie der Name schon sagt, scannt diese Aufgabe alle PHP-Dateien Ihres Themes und generiert aus den gettext-Aufrufen in den Dateien eine .pot-Datei (d. h. eine Übersetzungsdatei). Dies ist die WordPress-zentrierteste aller Aufgaben, die wir hier behandeln.
  • Watch-Aufgabe: Diese Aufgabe überwacht buchstäblich Änderungen an Ihren Dateien. Wenn eine Änderung vorgenommen wird, werden bestimmte Aufgaben ausgelöst und ausgeführt, abhängig von der Art der geänderten Datei.

Wenn wir beispielsweise eine JavaScript-Datei ändern, sollte die Scripts-Aufgabe ihre Magie wirken, und dann wäre es ideal, wenn der Browser automatisch für uns aktualisiert wird, damit wir diese Änderungen sehen können. Wenn wir außerdem eine PHP-Datei ändern, aktualisieren wir einfach den Browser, da PHP-Dateien nicht von anderen Aufgaben in unserem Projekt abhängen. Wir werden ein Gulp-Plugin namens Browsersync verwenden, um die Browseraktualisierungen zu handhaben, aber dazu und zu anderen Plugins kommen wir später.

  • Compress-Aufgabe: Wie Sie vielleicht erwarten, werden alle Plugins, die wir zum Schreiben unserer Aufgaben verwenden, mit npm verwaltet. Unser Theme-Ordner enthält also einen weiteren Ordner, wie node_modules, der wiederum Dateien wie package.json und andere Konfigurationsdateien enthält, die die Abhängigkeiten unseres Projekts definieren – und diese Dateien und Ordner werden nur während der Entwicklung benötigt. In der Produktion können wir die notwendigen Dateien für unser Theme herausnehmen und die unnötigen Entwicklungsdateien zurücklassen. Das wird diese Aufgabe tun; sie erstellt eine ZIP-Datei, die nur die notwendigen Dateien zum Ausführen unseres Themes enthält.

Als Bonusschritt für die Compress-Aufgabe: Wenn Sie ein Theme erstellen, das Sie auf WordPress.org oder vielleicht auf einer Website wie ThemeForest veröffentlichen möchten, dann wissen Sie wahrscheinlich bereits, dass alle Funktionen in Ihrem Theme mit einem eindeutigen Präfix versehen sein müssen.

function mythemename_enqueue_assets() {
  // function body
}

Wenn Sie also viele Themes erstellen. müssen Sie Funktionen in verschiedenen Themes einfach wiederverwenden können, aber das Präfix auf den Namen des Themes ändern, um Konflikte zu vermeiden. Wir können unsere Funktionen mit einem Platzhalterpräfix versehen und dann alle Instanzen dieses Platzhalters in der Compress-Aufgabe ersetzen. Wir können zum Beispiel den String _themename als Platzhalter wählen und dann beim Komprimieren unseres Themes alle ‚_themename‘-Strings durch den tatsächlichen Theme-Namen ersetzen.

function _themename_enqueue_assets() {
  // function body
}

Dies kann auch überall dort gelten, wo wir den Theme-Namen verwenden, zum Beispiel in der Textdomäne.

<?php _e('some string', '_themename'); ?>
  • Develop-Aufgabe: Diese Aufgabe tut nichts Neues. Sie wird ausgeführt, während wir unser Theme entwickeln. Sie bereinigt den dist-Ordner, führt die Styles-, Scripts-, Images- und Copy-Aufgaben im Entwicklungsmodus aus (d. h. ohne die Assets zu minimieren) und überwacht dann Dateiänderungen, um den Browser für uns zu aktualisieren.
  • Build-Aufgabe: Diese Aufgabe ist dazu gedacht, unsere Dateien für die Produktion zu erstellen. Sie führt dieselbe Bereinigung und dieselben Aufgaben wie die Develop-Aufgabe durch, jedoch im Produktionsmodus (d. h. sie minimiert die Assets im Prozess) und generiert eine neue POT-Datei für Übersetzungsaktualisierungen. Nach der Ausführung sollte unser dist-Ordner die für die Verteilung bereiten Dateien enthalten.
  • Bundle-Aufgabe: Diese Aufgabe führt einfach die Build-Aufgabe aus und stellt sicher, dass alle Dateien im dist-Ordner minimiert und für die Verteilung bereit sind. Dann führt sie die Compress-Aufgabe aus, die alle produktionsbereiten Dateien und Ordner zu einer ZIP-Datei bündelt. Wir wollen eine ZIP-Datei, da dies das Format ist, das WordPress zum Extrahieren und Installieren eines Themes erkennt.

So sieht unsere Dateistruktur nach Abschluss unserer Aufgaben aus

themefolder/
├── index.php
├── style.css
├── src/
└── dist/
    ├── images/
    │   └── cat.jpg // after compression
    ├── js/
    │   └── bundle.js // bundled with all imported files (minified in production)
    └── scss/
        └── bundle.scss // bundled with all imported files (minified in production)

Nachdem wir nun wissen, welche Aufgaben wir für unser Projekt verwenden werden und was sie tun, kommen wir zur Installation von Gulp in das Projekt.

Gulp installieren

Bevor wir Gulp installieren, sollten wir sicherstellen, dass wir Node und npm auf unseren Rechnern installiert haben. Das können wir tun, indem wir diese Befehle in der Befehlszeile ausführen

node --version
npm --version

...und wir sollten eine Versionsnummer erhalten, wie hier gezeigt

Nun, richten wir die Befehlszeile auf den Theme-Ordner aus

cd path/to/your/theme/folder

...und führen dann diesen Befehl aus, um ein neues npm-Projekt zu initialisieren

npm init

Dies wird uns mit einigen Optionen auffordern. Die einzige wichtige Option in unserem Fall ist die Option package name. Hier kann der Name des Themes angegeben werden – alles andere kann bei den Standardeinstellungen bleiben. Achten Sie bei der Wahl des Theme-Namens darauf, nur Kleinbuchstaben und Unterstriche zu verwenden und Bindestriche und Sonderzeichen zu vermeiden, da dieser Theme-Name zur Ersetzung des von uns zuvor erwähnten Funktionsplatzhalters verwendet wird.

Auf zur Installation von Gulp! Zuerst müssen wir die Gulp-Befehlszeilenschnittstelle (gulp-cli) global installieren, damit wir Gulp in der Befehlszeile verwenden können.

npm install --global gulp-cli

Danach führen wir diesen Befehl aus, um Gulp selbst im Theme-Verzeichnis zu installieren

npm install --save-dev gulp

Die aktuelle stabile Version von Gulp ist zum Zeitpunkt des Schreibens 3.9.1, aber Version 4.0 ist bereits im Projekt-Repository verfügbar.

Um sicherzustellen, dass alles korrekt installiert ist, führen wir diesen Befehl aus

gulp --version

Sehr gut! Es scheint, dass wir Version 4.0 verwenden, die zum Zeitpunkt des Schreibens die neueste Version ist.

Gulp-Aufgaben schreiben

Gulp-Aufgaben werden in einer Datei namens gulpfile.js definiert, die wir im Stammverzeichnis unseres Themes erstellen und platzieren müssen.

Gulp ist im Kern JavaScript, daher können wir eine schnelle Beispielaufgabe definieren, die etwas in die Konsole ausgibt.

var gulp = require('gulp');
gulp.task('hello', function() {
  console.log('First Task');
})

In diesem Beispiel haben wir eine neue Aufgabe definiert, indem wir gulp.task aufgerufen haben. Das erste Argument für diese Funktion ist der Name der Aufgabe (hello) und das zweite Argument ist die Funktion, die wir ausführen möchten, wenn dieser Name in der Befehlszeile eingegeben wird, was in diesem Fall "First Task" in die Konsole ausgeben sollte.

Machen wir das jetzt.

gulp hello

Hier ist, was wir bekommen

Wie Sie sehen, erhalten wir tatsächlich die gewünschte Ausgabe von console.log('First Task'). Allerdings erhalten wir auch einen Fehler, der besagt, dass unsere Aufgabe nicht abgeschlossen wurde. Alle Gulp-Aufgaben müssen Gulp mitteilen, wo die Aufgabe beendet werden soll, und das tun wir, indem wir eine Funktion aufrufen, die als erstes Argument in unserer Aufgabenfunktion übergeben wird, wie folgt:

var gulp = require('gulp');
gulp.task('hello', function(cb) {
  console.log('First Task');
  cb();
})

Versuchen wir erneut, gulp hello auszuführen, und wir sollten die gleiche Ausgabe erhalten, aber dieses Mal ohne den Fehler.

cb() ist eine Node.js-Callback-Funktion, die oft an eine asynchrone Funktion übergeben wird. Es gibt einige Fälle, in denen wir sie nicht aufrufen müssen, z. B. wenn eine Aufgabe ein Promise oder einen Node-Stream zurückgibt. Einen Node-Stream werden wir in den Aufgaben dieses Beitrags verwenden, was bedeutet, dass wir ihn in unserem Artikel oft sehen werden.

Hier ist ein Beispiel für eine Aufgabe, die ein Promise zurückgibt. In dieser Aufgabe müssen wir die cb()-Funktion nicht aufrufen, da Gulp bereits weiß, dass die Aufgabe endet, wenn das Promise aufgelöst wird oder ein Fehler auftritt.

gulp.task('promise', function(cb) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve();
    }, 300);
  });
});

Versuchen Sie nun, ‚gulp promise‘ auszuführen, und die Aufgabe wird ohne Fehler abgeschlossen.

Schließlich sei erwähnt, dass Gulp eine Standardaufgabe akzeptiert, die durch die alleinige Eingabe von gulp in der Befehlszeile ausgeführt wird. Dazu muss nur "default" als Aufgabenname-Argument verwendet werden.

gulp.task('default', function(cb) {
  console.log('Default Task');
  cb();
});

Nun wird die Aufgabe ausgeführt, wenn Sie gulp allein in der Befehlszeile eingeben.

Puh! Jetzt kennen wir die Grundlagen des Schreibens von Gulp-Aufgaben.

Es gibt noch eine Sache, die wir tun können, um die Dinge zu verbessern, und das ist die Aktivierung der ES6-Syntax in der Gulpfile. Dies ermöglicht uns die Verwendung von Features wie Destructuring, Import-Anweisungen und Pfeilfunktionen, unter anderem.

ES6 in der Gulpfile verwenden

Der erste Schritt zur Verwendung der ES6-Syntax in der Gulpfile ist die Umbenennung von gulpfile.js in gulpfile.babel.js. Wie Sie vielleicht schon wissen, ist Babel der Compiler, der ES6 nach ES5 kompiliert.

Installieren wir also Babel und einige seiner erforderlichen Pakete, indem wir ausführen

npm install --save-dev @babel/register @babel/preset-env @babel/core

Danach müssen wir eine Datei namens .babelrc in unserem Theme-Ordner erstellen. Diese Datei teilt Babel mit, welches Preset zum Kompilieren unseres JavaScripts verwendet werden soll. Der Inhalt der .babelrc-Datei wird wie folgt aussehen:

{
  "presets": ["@babel/preset-env"]
}

Jetzt können wir ES6 in unserer Gulpfile verwenden! Hier ist, wie das aussehen würde, wenn wir es neu schreiben würden:

import gulp from 'gulp';
export const hello = (cb) => {
  console.log('First Task');
  cb();
}

export const promise = (cb) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 300);
  });
};

export default hello

Wie Sie sehen können, importieren wir Gulp mit Import und nicht mit Require. Tatsächlich ist es nicht mehr nötig, Gulp zu importieren! Ich habe die Import-Anweisung trotzdem aufgenommen, um zu zeigen, dass sie anstelle von require verwendet werden kann. Wir müssen Gulp nicht importieren, da wir gulp.task nicht aufrufen müssen – stattdessen müssen wir nur eine Funktion exportieren, und der Name dieser Funktion ist der Name der Aufgabe. Außerdem reicht es aus, um eine Standardfunktion zu definieren, export default zu verwenden. Und beachten Sie auch die Pfeilfunktionen! Alles ist so viel prägnanter.

Lassen Sie uns weitermachen und die eigentlichen Aufgaben kodieren.

Entwicklung vs. Produktion

Wie wir bereits besprochen haben, müssen wir zwei Modi erstellen: **Entwicklung** und **Produktion**. Der Grund, warum wir zwischen den beiden unterscheiden müssen, ist, dass einige Details in unseren Aufgaben zeit- und speicherintensiv sind, was nur in einer Produktionsumgebung sinnvoll ist. Zum Beispiel muss die Styles-Aufgabe die CSS-Datei minimieren. Die Minimierung kann jedoch sowohl Zeit als auch Speicher beanspruchen – und wenn dieser Prozess bei jeder Änderung während der Entwicklung ausgeführt wird, ist das nicht nur unnötig, sondern auch sehr ineffizient. Idealerweise sollten Aufgaben während der Entwicklung so schnell wie möglich sein.

Wir müssen ein Flag setzen, das angibt, ob eine Aufgabe in einem oder dem anderen Modus ausgeführt werden soll. Wir können ein Paket namens yargs verwenden, das uns erlaubt, diese Art von Argumenten beim Ausführen eines Befehls zu definieren. Installieren wir es also und setzen es ein:

npm install --save-dev yargs

Jetzt können wir unserer Befehlszeile Argumente wie folgt hinzufügen:

gulp hello --prod=true

...und diese Argumente dann in der Gulpfile abrufen:

import yargs from 'yargs';
const PRODUCTION = yargs.argv.prod;

export const hello = (cb) => {
  console.log(PRODUCTION);
  cb();
}

Beachten Sie, dass die Werte, die wir in der Befehlszeile definieren, im yargs.argv-Objekt in der Gulpfile verfügbar sind und in console.log(PRODUCTION). In unserem Fall wird hier true ausgegeben, sodass PRODUCTION unser Flag sein wird, das entscheidet, ob eine Funktion innerhalb der Aufgaben ausgeführt wird oder nicht.

Wir sind bereit!

Wir haben hier viel Stoff behandelt, aber wir haben jetzt alles, was wir brauchen, um mit dem Schreiben von Aufgaben für unsere WordPress-Theme-Entwicklung zu beginnen. Das ist zufällig der einzige Fokus des nächsten Teils dieser Serie, also bleiben Sie dran für morgen.