SPA mit Vue.js, vuetify, Laravel Lumen und Passport III
Teil 3: Erstellung der Webanwendung mit Vue.js und axios
Nachdem wir uns in Teil 1 des Tutorials mit der Erstellung des Backends und der API mit Laravel Lumen beschäftigt haben, welches wir in Teil 2 mit einer Möglichkeit zur Authentifizierung versehen haben, werden wir uns in diesem Artikel mit der Erstellung der eigentlichen Vue.js Webanwendung bzw. des Clients beschäftigen.
Der etwas einfachere Weg wäre eine Initialisierung mittels vue-cli
, aber da selbst der offizielle Getting Started Guide davon abrät, habe auch ich mich entschieden, die Anwendung händisch aufzusetzen, damit ein größerer Lerneffekt entsteht.
Die aktuelle Projektstruktur
Durch die beiden ersten Teile haben wir momentan folgende durch Lumen erstellte Projektstruktur:
- app
- bootstrap
- config
- database
- public
- resources
- routes
- src
- storage
- tests
- vendor
.env.example
artisan
composer.json
composer.lock
phpunit.xml
Die Vue-Anwendung wird später parallel im public
Ordner liegen, vorher wird allerdings Node.js und npm benötigt, um die Abhängigkeiten des Clients aufzulösen.
Abhängigkeiten mit npm installieren
Im ersten Schritt initialisieren wir uns im root-Ordner unserer Anwendung npm.
npm init
Wir werden nun gebeten, ein paar Informationen zur Anwendung zu hinterlegen. Danach können wir die benötigten Pakete installieren.
vue
als Core-Paketvue-router
für das Routingaxios
für die Ajax-Requests an die API
npm install --save vue vue-router axios
Außerdem benötigen wir noch webpack zur Entwicklung incl. aller benötigter Loader und Abhängigkeiten.
webpack-cli
um webpack von der Kommandozeile aufzurufenwebpack-dev-server
für die lokale Entwicklungwebpack-merge
um eine Basiskonfiguration mit spziellen Konfigurationen der jeweiligen Umgebung (lokal, produktiv) zusammenzuführenhtml-webpack-plugin
um die von webpack erstellten Bundles in die Dateiindex.html
zu integrieren- Der Loader
vue-loader
als Präprozessor zur Kompilierung der verschiedenen Vue-Komponenten
npm install --save-dev webpack webpack-cli webpack-dev-server webpack-merge html-webpack-plugin vue-loader
Anlegen der Anwendungsstruktur
Nachdem alle Abhängigkeiten installiert wurden, können wir uns um die eigentliche Anwendungsstruktur im Ordner public
hinterlegen. Dabei wird folgendes benötigt:
- eine
index.html
Datei mit einem div-Container, in den die Vue.js-App gemounted werden kann - einen
src
Ordner, in dem unsere JavaScript- und Vue-Dateien liegen - eine
app.js
Datei, in der Vue.js initialisiert wird - eine Vue-Komponente
App.vue
, die letztlich die Inhalte ausgibt
Folgende Struktur ist somit vorgesehen:
...
public
|_ src
|_ js
|_ app.js
|_ App.vue
|_ index.html
...
package-lock.json
package.json
Die index.html
wird mit einem einfachen Grundgerüst befüllt. Wichtig dabei ist das app-div, in welche später die Vue-Anwendung geladen wird.
In der app.js
initialisieren wir Vue, laden die App.vue-Komponente und rendern diese in den DOM.
Die App.vue
Komponente belassen wir vorerst beim klassischen Hello, World!
Webpack einrichten und den webpack-dev-server starten
Nachdem wir nun die Anwendung aufgesetzt haben, müssen wir nur noch den webpack-dev-server für die lokale Entwicklung einrichten. Wir legen dafür im root-Verzeichnis zwei Dateien an:
webpack.config.js
als zentrale webpack-Konfigurationwebpack.dev.js
für die lokale Entwicklung
In der zentralen Konfiguration definieren wir den Einstiegspunkt, legen die Loader für die einzelnen Dateitypen fest und initialisieren die benötigten Plugins. Da wir in der app.js
Templates kompilieren und nicht direkt rendern, benötigen wir das Vue-Paket mit integrierten Compiler, welches wir per alias auflösen.
In der webpack.dev.js
mergen wir jetzt noch die entwicklungsspezifischen Anforderungen, insbesondere das Hot Module Replacement, wodurch geänderter Quellcode zu einem automatischen Nachladen des DOMs im Browser führt.
Zuguterletzt legen wir in der package.json noch einen Shortcut an, um den webpack-server zu starten.
{
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js --content-base public/ --history-api-fallback"
},
}
Der Befehl npm run dev
startet also unseren lokalen webpack-dev-server
mit der webpack.dev.js
Konfiguration und zeigt auf den Order public
, in welchem unsere Vue-Anwendung liegt. Der Parameter --history-api-fallback
greift schon vor auf das Routing und die gewünschte URL-Struktur. Mehr dazu im nächsten Abschnitt, aber erstmal testen wir nun unsere App durch Aufruf von http://localhost:8080
im Browser, wo daraufhin eine große Überschrift Hello, World!
erscheinen sollte.
Einrichtung des Routing und Anbindung dynamischer Inhalte via API
Nun wollen wir uns die Firmen und Produkte von der API abrufen und auf der Startseite ausgeben. Die Firmen und Produkte sollen dabei auf die dazugehörigen Detailseiten verweisen. Dafür ergänzen wir die aktuelle Anwendungsstruktur um folgende Elemente:
- ein Ordner
pages
für alle Top Level Komponenten, die mit einer Route verknüpft sind - eine
router.js
Datei imsrc\js
Ordner, in der wir die Routen und damit die URLs festlegen
Damit sieht die Ordnerstruktur wie folgt aus:
...
public
|_ src
|_ js
|_ app.js
|_ router.js
|_ pages
|_ Company.vue
|_ Home.vue
|_ Product.vue
|_ App.vue
|_ index.html
...
package-lock.json
package.json
Wir fangen damit an, die router.js
mit den benötigten Routen zu belegen. Wir haben dabei, um es anfangs übersichtlich zu halten, eine Route für die Startseite, eine Route für die Firmendetailseite und eine für die Produktdetailseite. Wichtig ist hier noch der Modus des Routers history
der URLs ohne Fragmente (#) erstellt, weswegen wir im vorherigen Schritt schon entsprechende Vorkehrungen beim webpack-dev-server getroffen hatten.
Den Router müssen wir nun bei der Initialisierung der Anwendung berücksichtigen, indem wir ihn der app.js
importieren. In diesem Zusammenhang initialisieren wir auch axios, um die Requests an die API durchzuführen. Hierbei ist wichtig, dass wir die axios-Instanz global in den Vue-Komponenten verfügbar machen und die Basis-URL der API festlegen. Der Router wird dann der Vue-Anwendung mit übergeben.
Erstellung der Vue-Komponenten und Anbindung der API
Nun müssen wir noch die Vue-Komponenten erstellen, die im Router referenziert sind. Wir fangen an mit den beiden Komponenten für die Firmen- und die Produktseite. Die beiden Seiten sind relativ identisch aufgebaut. Im script
Bereich der Seite wird, nach dem Mounting (siehe Dokumentation), mittels axios bzw. this.$http
der Endpunkt der API aufgerufen, wobei die ID aus der URL bzw. der Route übergeben wird. Die erhaltene Antwort wird an das data-Objekt übergeben. Dieses wird dann im Template ausgegeben, sofern das Produkt/die Firma vorhanden ist.
Hinweis: Der Request an die API wird im ersten Versuch fehlschlagen, da die API einen CORS-Fehler zurückgeben wird. Grund ist, dass die Anfrage von einem anderen Port kommt. Man kann diese Problematik mittels .htaccess
lösen, oder man integriert eine weitere CORS-Middleware in Lumen. Dafür erstellt man unter app\http\Middleware
eine CorsMiddleware.php
mit folgenden Inhalt (der Access-Control-Allow-Origin
muss produktiv entsprechend angepasst werden):
Diese Middleware muss nun in der bootstrap\app.php
noch registiert werden.
$app->middleware([
App\Http\Middleware\CorsMiddleware::class,
]);
Erstellung der Startseite
Nun da die Requests letztlich funktionieren sollten, erstellen wir uns eine Startseite, auf der alle Firmen und Produkte, jeweils verlinkt, ausgegeben werden. Wir nutzen hierfür mehrere parallele Anfragen an die API mittels sogenanntem spreading bei axios (siehe Dokumentation). Über diese Einträge wird dann mittels v-for
geloopt und für jedes Element ein verlinktes Listenelement mittels router-link
ausgegeben. Diese Links gehen alle auf die jeweiligen benamten Routen (mittels name
) und übergeben dort den Parameter id
.
Als letztes müssen wir jetzt noch die App.vue
anpassen, die nun nicht mehr Hello, World!
ausgeben soll, sondern die Vue-Komponente, die durch das Routing angesprochen ist.
Wenn nun der webpack-dev-server läuft und man widerum http://localhost:8080
aufruft, sollten 2 Listen mit Firmen und Produkten kommen, die jeweils verlinkt sind. Die Produkte und Firmen kann man anklicken oder direkt mit z.B. http://localhost:8080/products/1
bzw. http://localhost:8080/companies/1
aufrufen.
Ausblick
Im nächsten Teil werden wir vuetify einbinden, um die ganze Seite etwas aufzuhübschen und eine angemessene Navigation zu integrieren. Außerdem werden wir ein Kontaktformular erstellen, welches verlinkt ist und an die API gesendet werden kann.