WordPress-Plugin-Admin-UI-Entwicklung mit vue.js – da wächst nichts zusammen, was nicht zusammen gehört

“Jetzt wächst zusammen, was zusammen gehört”. Ob das berühmte Zitat von Willy Brandt nun älter ist als meist angenommen oder nicht, mögen die Historiker entscheiden. Schließlich geht es hier um clientseitige JavaScript-Programmierung, und noch dazu um das aufstrebende JavaScript-Framework namens vue.js. Dies hat in letzter Zeit für Furore gesorgt, weshalb sich auch Myriaden von Einführungen und Artikeln darüber finden lassen, die zumindest über die ersten Hürden prima weiterhelfen. 

Ich beschäftige mich erst seit kurzer Zeit mit vue.js, primär im Rahmen für ein anderes Projekt, aber als Test-Case wollte ich probieren, wie sich vue.js & Co. im Rahmen der Erstellung einer Admin-UI für ein WordPress-Plugin nutzen lassen. Um es vorweg zu nehmen – es funktioniert. Um es ebenfalls vorweg zu nehmen – ja, aber…

WordPress-Plugins und deren Admin-UI

So gut wie jedes WordPress-Plugin besitzt eine oder mehrere Seiten, auf denen man jeweils Einstellungen für das Plugin vornehmen kann. Beispielsweise kann man im Redis-Cache-Plugin selbiges aktivieren oder deaktivieren, des Weiteren lässt sich eine Diagnose anzeigen. Hier beschränkt sich die Admin-UI auf eine Seite, ist somit eher einfach gehalten, weil auch nicht viel konfiguriert werden muss. Ein ganz anderes Kaliber ist etwa das BackWPup-Plugin zur Erstellung von WordPress-Backups – hier wird eine Art Assistent angeboten, mit dessen Hilfe sich auch komplexe Backup-Szenarien erstellen lassen.

Im Folgenden bezeichne ich derartige Seiten der Einfachheit halber als Admin-UI, denn die Einstellungsseiten der Plugins integrieren sich in die Verwaltungsoberfläche von WordPress. Nun lassen sich derartige Admin-UIs auf vielerlei Arten erstellen, von reinem PHP ohne Beteiligung von JavaScript, über die Verwendung von underscore.js-Templates bis hin zur Integration vollständiger JavaScript-Frameworks dürfte einem in freier Wildbahn so gut wie alles begegnen. Da die WordPress-Entwickler zwar einige JavaScript-Libraries per Default integrieren, so dass sich diese recht einfach hinzufügen lassen, ansonsten aber so gut wie keine Vorgaben vorhanden sind, verwendet gefühlt jedes WordPress-Plugin eine andere Technologie. Daran ist auch gar nichts weiter schlimm, sofern sich die Frameworks nicht ins Gehege kommen – dazu vielleicht an anderer Stelle mehr. Tatsächlich ist mir sogar vue.js bereits als Grundlage einer Admin-UI eines WordPress-Plugins begegnet, somit schien auch das durchaus keine so abwegige Idee gewesen zu sein. 

Ein minimales Beispiel

Für mein Plugin hatte ich zunächst nur PHP und ein wenig jQuery eingesetzt – die Konfigurationsparameter werden in einem einfachen Formular übergeben, dazu war noch gar kein JavaScript notwendig. Auf einer Seite kann der User jedoch die Verbindung zum konfigurierten Server prüfen, an der Stelle wollte ich klassisches “Ajax“, oder vielmehr “Ajaj”, also einen asynchronen HTTP-Request, dessen Rückgabe im JSON-Format stattfindet, einsetzen.

Screenshot Plugin – Aktion “Check connection”

 

Screenshot Plugin – “Connection test successful”

 

Im Laufe der Entwicklung sind natürlich noch weitere Aktionen hinzu gekommen, doch dies war zunächst die Initialzündung, um JavaScript zu nutzen. Der erste Weg lautete somit, jQuery zu verwenden, einen Event an den Prüfen-Button zu binden und das Ergebnis darzustellen. Die dafür notwendige JavaScript-Datei, genannt “admin.js”, war ebenfalls schnell eingebunden – die Action admin_enqueue_scripts wird genutzt, um eine Methode aufzurufen, in der wiederum wp_enqueue_scripts die entsprechende JavaScript-Datei einbaut. 

Erste Experimente mit vue.js für die Admin-UI

So weit, so gut, doch warum wollte ich nun vue.js verwenden, denn mit purem jQuery wäre ich vermutlich zunächst schneller zum Ziel gekommen? Ganz einfach – um Erfahrungen mit vue.js zu sammeln und um es einfach auszuprobieren. Im ersten Schritt hatte ich einerseits die benötigten Dateien vue.js, vue-router.js und weitere dem Admin-Bereich hinzugefügt, andererseits den neuen Code in die bestehende “admin.js“-Datei geschrieben. Insgesamt bin ich somit ähnlich wie in der vue.js-Dokumentation unter “Installation – Direct <script> include” und “Components Basics – Base Example” zu lesen ist, vorgegangen. Dass das nicht der Weisheit letzter Schluss war, lag auf der Hand.

Ein wenig mehr Struktur darf’s sein!

Denn insbesondere durch das Konzept der Single File Components wird eine bestechend einfache Strukturierung der Anwendung erreicht. Eine Komponente besteht dabei aus den Abschnitten (HTML-)Template, JavaScript und CSS und besitzt die Dateiendung “.vue”, wobei Syntax-Highlighting etc. von allen gängigen Editoren per Plugins unterstützt wird. Beispielsweise könnte die Komponente alle Bestandteile für den oben erwähnten Check der Server-Verbindung beinhalten. Das wäre der HTML-Code zur Einbindung des Buttons, der JavaScript-Code, um bei Klick auf den Button eine Aktion auszulösen (Event-Binding), die Reaktion darauf, d.h. den HTTP-Request und die Darstellung des Ergebnisses, die letztlich nur eine Zustandsänderung einer Variable im Model (View-Binding) anzeigt. Im Code betrachtet sieht der Template-Bereich wie folgt aus:

Beim Laden der Seite ist checkStatus leer, insofern wird nur der Button angezeigt. Die Methode checkConnection wiederum setzt nach der erfolgter Prüfung je nach Ergebnis diese Variable auf “success” oder “error“, spasseshalber wird während der Prüfung noch ein üblicher Wartekreis (“Spinner”) angezeigt. Eigene CSS-Definitionen benötigt die Komponente nicht, insofern ist der style-Bereich einfach leer. 

Eine JavaScript-Entwicklungumgebung muss her!

Klingt alles prima, doch diese Lösung hat natürlich auch einen Haken. Denn was soll der Browser mit einer .vue-Datei anfangen? Wie lässt sich daraus eine valide JavaScript-Datei erstellen, die im Browser geladen werden kann und die der Browser vor allem interpretieren kann? So bequem wie die Entwicklung jener Single File Components ist, so gruselig oder sagen wir zumindest aufwändig ist der Aufbau der Entwicklungs-Infrastruktur, die für diesen Weg notwendig ist. Wie man sich bereits denken kann, kommt dabei npm zum Einsatz, also musste erstmal node.js installiert werden. Und für die Entwicklung mit vue.js steht eine CLI zur Verfügung, so dass man ein neues vue.js-Projekt per Kommandozeile in wenigen Minuten mit geringem Aufwand erzeugen kann. Danach besitzt man ein Standard-Verzeichnisschema (Boilerplate) mit einer Beispiel-Anwendung, mit der sich sofort loslegen lässt. Das beinhaltet auch einen Entwicklungsserver, der mit dem Kommando “npm run serve” gestartet wird, dabei werden alle Komponenten in JavaScript-Dateien “kompiliert”, Sass wird zu CSS, am Ende wird ggf. noch minifiziert, gepackt usw., und die fertige Anwendung kann sofort auf dem Port 8080 (Default-Port) betrachtet werden. Einfach, bequem usw..

Richtig, wenn man eine vue.js-Anwendung from scratch erstellt. Wenn man sich diesen Luxus können kann – perfekt. Aber genau das ist eben nicht immer möglich, so auch nicht in diesem Fall. Schließlich musste die entsprechende JavaScript-Datei vom zugrunde liegenden WordPress-Entwicklungsserver ausgeliefert werden können – ein node-Server auf Port 8080 nützte mir genau nullkommanix. Ebenso wenig habe ich eine HTML-Datei benötigt (im vue.js-Boilerplate ist eine solche Einstiegsseite (index.html) enthalten), schließlich wird alles HTML vom guten, alten PHP erzeugt. Kurzum – es musste ein Weg gefunden werden, die Werkzeuge der modernen JavaScript-Entwicklung in die WordPress-(Entwicklungs-)Server-Infrastruktur zu integrieren. 

Von JavaScript-Tools…

Ich habe mir daraufhin erstmal die in Frage kommenden JavaScript-Entwicklungs-Tools angesehen und mir einen Überblick verschafft. Der Weg, zunächst über die vue-CLI eine Anwendung zu erstellen und aus dieser die nicht benötigten Teile heraus zu lösen, scheiterte schnell. Es waren zu viele Abhängigkeiten, zu viele mir bis dato unbekannte Module, die sicherlich ihren Sinn und Zweck erfüllen, aber hier nicht notwendig waren. Insofern entschloss ich mich dazu, den umgekehrten Weg zu gehen, d.h. von einem Minimum an Tools ausgehend ggf. weitere hinzuzufügen, bis alles einsatzfähig war. Schnell war der Einsatz von webpack klar, mit diesem Module-Bundler sollten sich die .vue-Dateien zusammenpacken, Abhängigkeiten auflösen lassen usw.. Auf Tools wie gulp oder grunt wollte ich gerne verzichten, statt dessen auf NPM-Scripts setzen. Hingegen wollte ich gerne neue Features von ECMAScript 2015 aufwärts einsetzen, ein JavaScript-Transpiler musste her, damit alte Browser neueren Code verstehen, ergo sollte auch Babel zum Einsatz kommen. Und natürlich vue.js und ggf. ein paar vue.js-Komponenten, beispielsweise die Wartekreis-Komponente

Damit hatte ich den minimalen Umfang festgelegt. Die Filesystem-Struktur sollte ähnlich sein wie bei einer per vue-CLI angelegten Anwendung:

Im Verzeichnis “dist” sollte die resultierende JavaScript-Datei erzeugt werden, die vom PHP-Web-Server ausgeliefert wird. HTML oder CSS-Dateien sind hier nicht enthalten. Das Verzeichnis “jsapp” befindet sich dabei im Hauptverzeichnis des Plugins, so dass während der Entwicklung die JavaScript-Datei in “dist” erreicht werden kann. Bei Veröffentlichung des Plugins soll hingegen der JavaScript-Quellcode nicht mitgeliefert werden – nicht aus Gründen der Verschleierung, sondern weil schlicht und einfach nicht notwendig, außerdem würde das Plugin dadurch wesentlich mehr Speicherplatz benötigen. In einem später zu definierenden Schritt müsste somit die fertige JavaScript-Datei z.B. im “js“-Verzeichnis des Plugins platziert werden, danach kann “jsapp” entfernt werden. 

…und der npm-Hölle

Zum Aufbau der JavaScript-Entwicklungs-Infrastruktur habe ich mir auch mehreren Blog-Einträge und Artikel durchgelesen. Nachdem einer davon recht vielversprechend aussah, wollte ich mich anhand des Beispiels zu einer Entwicklungsumgebung hangeln. Der erste Schritt war natürlich die Installation von node.js, womit NPM ebenfalls installiert wurde. Hier habe ich mich des aktuellen Repositories bedient. Ich installierte danach somit einige vorgeschlagene npm-Pakete, startete zwecks Test das Kommando zur Kompilierung und – rumms. Versionskonflikt, Babel möchte in der aktuellen Version 7 installiert werden, bitte, danke. Also Version 7 installiert – Versionskonflikt, babel-loader verträgt sich nicht mit Version 7, dazu muss wiederum ein anderes Paket installiert werden – waaaahhhh! Ok, an der Stelle waren die Wiederholungen der Zeichen noch nicht so viele, das änderte sich jedoch, je weiter ich die diversen Hinweise befolgte. Letztlich ließen sich webpack, babel. npm und wie sie alle heißen, nicht dazu überreden, auch nur einen Schritt weiter zu laufen als bis zur Ausgabe der Fehlermeldungen.

Ich habe mir natürlich noch weitere Artikel angesehen, die nicht minder vielversprechend klangen, teilweise waren komplett andere Wege beschrieben, teilweise sehr ähnliche, mal mit neueren Versionen, mal mit älteren… Natürlich ist die Entwicklung im Bereich von JavaScript-Tools sehr dynamisch, genau das ist Fluch und Segen zugleich. Wahrscheinlich sind auch die Hinweise in diesem Artikel hier schon veraltet, bevor ich ihn veröffentlicht habe…

Daher will ich auch nicht auf jeden Installationsschritt Bezug nehmen, letztlich habe ich mit “npm init” begonnen, womit initial nach Beantwortung einiger Fragen (Name der Anwendung, Version, Beschreibung usw.) die zentrale Paketbeschreibungsdatei “package.json” angelegt wurde. Danach habe ich die minimal benötigten Tools wie webpack, babel und vue installiert, wodurch die package.json wiederum geändert wurde, d.h. die neuen Pakete wurden innerhalb der Abhängigkeiten hinzugefügt, so dass jederzeit eine Wiederherstellung derselben Umgebung möglich ist (npm install). Dabei ist noch zu unterscheiden zwischen devDependencies, also Abhängigkeiten, die für den Development-Prozess relevant sind und Abhängigkeiten (dependencies), also Module, die später in der fertigen JavaScript-Anwendungsdatei Platz nehmen müssen. Letztere werden mit “npm install --save <paketname>” installiert, erstere mit dem Parameter “--save-dev“. 

Letztlich sind die hier vorgestellten Dateien die Essenz aus einigem Suchen in diversen Anleitungen und Artikeln und Kopieren und Anpassen von Beispielen. Während dieser Schritte habe ich mich durchaus gefragt, ob sich überhaupt noch jemand mit der gesamten NPM&Co.-JavaScript-Tool-Infrastruktur auskennt, oder ob genau diese Art und Weise – eine Schleife aus “copy&paste&modify” nicht vielmehr inzwischen zum Standard geworden ist. Natürlich könnte ich behaupten, dass ich sämtliche Dokumentation von Webpack, Babel, NPM & Co. gelesen und daraufhin aus dem Stegreif mal eben nebenbei die Config-Dateien erstellt hätte…  Weit gefehlt! So schön und praktisch die diversen NPM-Pakete mit deren Modulen auch sind, die Abhängigkeiten könnten direkt aus der Hölle entstammen. Beispiel:

Das heißt: Insgesamt habe ich 11 Pakete per npm installiert. Die Installation dieser 11 Pakete brachte jedoch die Installation von 369 Paketen mit sich, ergo wurden 358 Pakete als Abhängigkeiten installiert. Die Anzahl der einzelnen Dateien ist auch einen Blick wert:

Zugegeben, im Output von “ls -lR” sind auch ein paar Leerzeilen und Verzeichnisnamen, aber selbst wenn man 10% von der Anzahl der Dateien abzieht, sind es noch über 10000 Dateien, die im node_modules-Verzeichnis enthalten sind – und das nur zum Aufbau der Development-Infrastruktur. Dabei besitzt das node_modules-Verzeichnis einen Umfang von 65MB! 

Aber Speicherplatz ist günstig und immerhin wird damit nur der jeweilige Entwicklungs-Rechner belastet, dank des automatischen Auflösens der Abhängigkeiten während der Installation muss der ganze Krempel wenigstens nicht im Git-Repository landen. 

Ziele der JavaScript-Entwicklungs-Infrastruktur

Kurz durchgeatmet und rekapituliert – was wollte ich erreichen?

  1. Entwicklung von vue.js Single File Components in einer möglichst ähnlichen Struktur wie vom vue.js-Boilerplate/CLI vorgegeben
  2. Generierung einer JavaScript-Datei für die Produktivumgebung
  3. möglicht bequeme Entwicklungsumgebung, d.h. Quellcode-Änderungen der JavaScript bzw. vue-Dateien innerhalb der Plugin-Admin-UI sollten beobachtet werden, so dass die resultierenden JavaScript-Dateien neu erzeugt und somit beim nächsten Request geladen werden können

Zur Struktur hatte ich mich bereits geäußert, für den zweiten Schritt sollte sich webpack zuständig zeigen. Der dritte Schritt war sogar einfacher als anfangs vermutet, dazu gleich mehr. 

Konfigurationsdateien

package.json (npm)

Im Folgenden die package.json-Datei, die den aktuellen Entwicklungsstand widerspiegelt:

webpack.config.js (webpack)

Dazu ist noch eine Konfigurationsdatei für Webpack mit dem nahe liegenden Namen webpack.config.js vorhanden:

In der webpack.config.js wird definiert, aus welchen Dateien / Pfaden unter Zuhilfenahme von Modulen / Loadern mit welchen Regeln welche Datei(en) erzeugt werden. Beim späteren Deployment sollten jedoch die “development“-Einstellungen geändert werden, der Default-Mode wäre für Produktivsysteme geeignet, während dessen Debugging-Möglichkeiten im Browser sehr eingeschränkt würden. So kurz und knapp ist die Datei fast selbsterklärend, es werden nur die Module babel und vue-loader verwendet, der Einstiegspunkt ist die Datei index.js im Hauptverzeichnis des JavaScripts, und am Ende wird die Datei main.js im Verzeichnis “dist” erstellt. 

Noch kürzer ist die package.json. Deren Kopf wurde weitestgehend von npm mit dem init-Kommando generiert. Die Abhängigkeiten für die Produktion beschränken sich auf vue, vue-router und vue-simple-spinner – diese drei Komponenten sollen eingesetzt werden. Alle anderen Abhängigkeiten spielen nur während der Entwicklung eine Rolle, hier finden sich die bereits angesprochenen Tools wieder. 

npm-Scripts

Interessanter ist der scripts“-Bereich. Die “test“-Direktive lassen wir mal beiseite, auch diese wurde während der Generierung per “npm init” erzeugt. Das Kommando “build“, d.h. vollständig auf der Kommandozeile “npm run build” geschrieben, sorgt für das Zusammenpacken, den Transpile-Schritt und somit für die Generierung der JavaScript-Anwendungsdatei. Noch nicht eingeflossen sind die Schritte zur Minimierung, auch damit werde ich mich beschäftigen, wenn es an der Zeit ist.

Einbau in WordPress

Die erzeugte Datei musste dann nur noch im Rahmen von WordPress eingebunden werden:

Leider hatte der Prozess einen Nachteil – die Generierung der JavaScript-Datei verschlang jedes Mal mehrere Sekunden. Das klingt nicht nach viel, davon abgesehen musste der Schritt nach jeder Änderung manuell angestoßen werden. Bei Erzeugung eines Projektes mit der vue-CLI ist hingegen bereits ein Watch-Mode eingebaut, der Änderungen der Dateien beobachtet und in Sekunden(bruchteilen) zu aktualisierten Resultaten führt.

Glücklicherweise ist dieser Watch-Mode bereits Bestandteil von webpack und lässt sich per Kommandozeilen-Option “--watch” aktivieren. Damit wird webpack gestartet, woraufhin alle relevanten Quelldateien unter Beobachtung stehen. Bei Änderung wird automatisch neu kompiliert. Als NPM-Kommando im “scripts“-Bereich von package.json eingebaut lässt sich dies einfach mit “npm run watch” aufrufen. Eine Neukompilierung dauerte danach gerade noch eine halbe Sekunde, damit ließ sich nun wesentlich flüssiger arbeiten. 

Fazit

Insgesamt hatte ich nun eine Umgebung, die die anfangs erwähnten Ziele erfüllen konnte. Sicherlich bieten vue.js, aber auch die JavaScript-Development-Tools noch einige Geheimnisse, ich hatte ja gerade einmal die Oberfläche angekratzt. Wobei die Vielfalt oder sagen wir neudeutsch “Diversity” der Tools insgesamt schon verwirrend sein kann. Immerhin kristallisieren sich einige Standard heraus, an webpack dürfte wohl kein Weg vorbei führen, ebenso wenig an babel. Die Entwicklung mit vue.js im Rahmen einer Admin-UI für ein WordPress-Plugin ist mit den hier vorgestellten Verfahren durchaus als angenehm zu bezeichnen – wenn man die NPM&Co.-Hölle erst einmal durchquert hat. Und wie immer gilt – all dies ist noch stark in Entwicklung. Sowohl in Bezug auf die Tools, ich verwende zwar aktuell die neuesten Versionen, aber irgendwo lauert schon wieder vue.js in der Version 3, lange wird es nicht mehr dauern.

Die hier vorgestellten Mittel und Wege sind auch nur ein Beispiel, ich behaupte nicht, es wäre der einzige, geschweige denn der beste Weg, innerhalb von WordPress eine Admin-UI mit vue.js zu bauen. Insofern herrscht die Warnung – “works for me“, unter den bereits genannten Prämissen, mit den angegebenen Versionen usw.. Die Art und Weise der Entwicklung mit vue.js gegenüber der Entwicklung ohne vergleichbare Frameworks bzw. nur mit jQuery o.ä. erfordert auch ein gewisses Umdenken, was aber relativ leicht fallen sollte. Und auch wenn der Einsatz von vue.js für diese Admin-UI innerhalb eines WordPress-Plugins noch nicht notwendig gewesen war bzw. die ersten Schritte von einigem Overhead begleitet waren, hat es sich dennoch gelohnt, sich einmal damit zu beschäftigen. Für weitere bzw. insbesondere komplexere Web-Anwendungen, die viel Aktivität auf Client-Seite benötigen, werde ich vue.js definitiv in Betracht ziehen. Insofern wächst mit der Zeit sogar doch das zusammen, was so gar nicht zusammen zu gehören scheint. 

 

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Tags: