Mehrseitige Anwendungen (MPAs) werden von Tag zu Tag weniger beliebt. Berühmte Plattformen wie Facebook, Twitter, YouTube, Github und viele andere nutzen stattdessen bereits die Single-Page-Application-Technologie (SPA).

Diese trendige Technologie ermöglicht es den Nutzern, schnell und reaktionsschnell mit Webanwendungen zu arbeiten, da alles auf der Client-Seite gerendert wird. Für Entwicklerinnen und Entwickler, die mit Frameworks wie Laravel oder Django serverseitig gerenderte Anwendungen erstellen, kann dies jedoch ein Problem darstellen.

Zum Glück ist Inertia.js zur Stelle und kommt zur Rettung.

In diesem Artikel zeigen wir dir, wie du mit Inertia.js loslegen kannst und wie du es mit Laravel, Vue.js und Tailwind CSS verwendest, um eine moderne Blog-Webanwendung zu erstellen. Außerdem verraten wir dir, wie du SPAs SEO-freundlicher gestalten kannst, und verraten dir noch ein paar andere Tricks.

Wenn du gerade erst mit Laravel anfängst, empfehlen wir dir, zuerst diesen Artikel zu lesen, damit du gleich loslegen kannst.

Warum SPA?

Bevor wir fragen können, warum wir Inertia nutzen sollten, müssen wir zuerst fragen: „Warum SPA?“

Warum sollte jemand client-seitig gerenderte Anwendungen den traditionellen server-seitigen Anwendungen vorziehen? Was würde einen Full-Stack-Laravel-Entwickler dazu bringen, sich von Blade-Komponenten zu verabschieden?

Die kurze Antwort: Weil Geschwindigkeit und Reaktionsfähigkeit für eine erfolgreiche Nutzeransprache unerlässlich sind.

Bei MPAs sendet der Browser ständig Abfragen an das Backend, das dann zahlreiche Datenbankabfragen ausführt. Nachdem die Datenbank und der Server die Abfragen verarbeitet und an den Browser geliefert haben, wird die Seite gerendert.

SPAs sind jedoch anders. Die Anwendung bringt alles, was der/die Nutzer/in benötigt, direkt auf die Seite, sodass der Browser keine Abfragen mehr senden oder die Seite neu laden muss, um neue HTML-Elemente zu rendern.

Wegen dieses einzigartigen Nutzererlebnisses drängen viele namhafte Unternehmen darauf, ihre Websites in Single-Page-Anwendungen umzuwandeln.

Allerdings kann es für Laravel-Entwicklerinnen und -Entwickler schwierig sein, eine Single-Page-Anwendung zu erstellen, weil sie dann Vue.js oder React anstelle von Blade-Templates verwenden müssten, wodurch viele Laravel-Vorzüge verloren gehen, die Zeit und Mühe sparen.

Mit Inertia.js hat sich das jedoch geändert.

Warum Inertia?

Wenn Laravel-Entwickler, vor Inertia, Web-SPAs mit Vue bauen wollten, mussten sie APIs einrichten und JSON-Daten mit Laravel zurückgeben, und dann etwas wie AXIOS verwenden, um die Daten in Vue-Komponenten abzurufen. Außerdem bräuchten sie etwas wie den Vue Router, um Routen zu verwalten, was bedeuten würde, dass Laravel-Routing sowie Middlewares und Controller wegfallen würden.

Inertia.js hingegen ermöglicht es Entwicklern, moderne einseitige Vue-, React- und Svelte-Apps mit klassischem serverseitigem Routing und Controllern zu erstellen. Inertia wurde für Laravel-, Ruby on Rails- und Django-Entwickler entwickelt, damit sie Apps erstellen können, ohne ihre Programmiertechniken wie das Erstellen von Controllern, das Abrufen von Daten aus einer Datenbank und das Rendern von Ansichten zu ändern

Dank Inertia.js werden sich Laravel-Entwickler wie zu Hause fühlen.

Wie Inertia funktioniert

Wenn du eine SPA nur mit Laravel und Vue erstellst, erhältst du zwar eine vollständige JavaScript-Seite für dein Frontend, aber das bedeutet nicht, dass du eine Single-Page-App erleben kannst. Jeder angeklickte Link führt dazu, dass dein clientseitiges Framework beim nächsten Laden der Seite neu gestartet wird.

An dieser Stelle kommt Inertia ins Spiel.

Inertia ist im Grunde eine Client-seitige Routing-Bibliothek. Sie ermöglicht es dir, zwischen Seiten zu navigieren, ohne die gesamte Seite neu laden zu müssen. Dies wird über die Komponente <Link> erreicht, die ein leichtgewichtiger Wrapper um ein Standard-Anker-Tag ist.

Wenn du auf einen Inertia-Link klickst, fängt Inertia den Klick ab und leitet dich stattdessen zu XHR weiter. Der Browser lädt die Seite auf diese Weise nicht neu und bietet dem Nutzer ein vollständiges Single-Page-Erlebnis.

Erste Schritte mit Inertia

Eine einfache Seite mit "Kinsta Blog" in einem blauen Banner am oberen Rand und einer einzelnen Reihe von Beispielartikelkarten
Eine mit Inertia.js erstellte Beispielseite

Um Inertia und seine Integration in Laravel zu verstehen, werden wir eine Blog-Web-App namens Kinsta Blog erstellen und dabei die mächtigste Kombination verwenden: Laravel für das Backend, Vue.js für das JavaScript-Frontend und Tailwind CSS für das Styling.

Wenn du dieses Tutorial lieber in einer lokalen Umgebung verfolgen möchtest, kannst du DevKinsta verwenden, ein leistungsstarkes Tool für Entwickler, Designer und Agenturen, mit dem du ein- und mehrseitige WordPress-Webanwendungen erstellen kannst. Glücklicherweise lässt sich WordPress mit dem Corcel-Paket leicht in Laravel integrieren.

Voraussetzungen

Um diese Tutorial optimal nutzen zu können, solltest du mit Folgendem vertraut sein:

  • Laravel-Grundlagen (Installation, Datenbank, Datenbankmigrationen, Eloquent Models, Controller und Routing)
  • Vue.js-Grundlagen (Installation, Struktur und Formulare)

Wenn du dir unsicher bist, schau dir diese fantastischen kostenlosen und kostenpflichtigen Laravel-Tutorials an. Ansonsten lass uns loslegen.

Schritt 1: Installation der Kernelemente

Damit du dich auf Inertia.js konzentrieren kannst und der Spaßfaktor nicht zu kurz kommt, solltest du die folgenden Einstellungen bereithalten:

  1. Ein frisch installiertes Laravel 9 Projekt mit dem Namen kinsta-blog
  2. Tailwind CSS CLI installiert in unserem Laravel-Projekt
  3. Bilddatei „kinsta-logo.png“. Lade und entpacke das Kinsta-Logo-Paket von https://kinsta.com/press/ Seite herunter und kopiere kinsta-logo2.png in das Verzeichnis public/images als kinsta-logo.png.
  4. Zwei Blade-Komponenten in kinsta-blog/resources/views für die Anzeige der Blog-Startseite und eines einzelnen Artikels im Blog wie unten gezeigt:“/resources/views/index.blade.php„:
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    
        <title>Kinsta Blog</title>
      </head>
    
      <body>
        <header>
          <h1>Kinsta Blog</h1>
        </header>
    
        <main>
          <h2>Read our latest articles</h2>
    
          <section>
            <article>
              <div>
                <img src="/images/kinsta-logo.png" alt="Article thumbnail" />
              </div>
    
              <h3>Title for the blog</h3>
              <p>
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem
                itaque error vel perferendis aliquam numquam dignissimos, expedita
                perspiciatis consectetur!
              </p>
    
              <a href="#">Read more</a>
            </article>
          </section>
        </main>
    
        <footer>
          <h2>Join our Newsletter</h2>
    
          <input type="email" />
        </footer>
      </body>
    </html>

    „/resources/views/show.blade.php„:

    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    
        <title>Kinsta Blog</title>
      </head>
    
      <body>
        <main>
          <article>
            <div>
              <img src="/images/kinsta-logo.png" alt="Article thumbnail" />
            </div>
    
            <h1>Title for the blog</h1>
    
            <p>Article content goes here</p>
          </article>
        </main>
    
        <footer>
          <h2>Join our Newsletter</h2>
    
          <input type="email" />
        </footer>
      </body>
    </html>
  5. Die lokale MySQL-Datenbank mit dem Namen kinsta_blog ist mit unserem Projekt verbunden: „.env„:
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=kinsta_blog
    DB_USERNAME=root
    DB_PASSWORD=
  6. Artikelmodell, Migrationen und Factories:
    „app/Models/Article.php„:
    <?php
    
    namespace AppModels;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Article extends Model
    {
        use HasFactory;
    
        protected $fillable = ['title', 'excerpt', 'body'];
    }

    „database/migrations/create_articles_table.php„:

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    return new class extends Migration
    {
    
        public function up()
        {
            Schema::create('articles', function (Blueprint $table) {
                $table->id();
                $table->string('title');
                $table->text('excerpt');
                $table->text('body');
                $table->timestamps();
            });
        }
    
        public function down()
        {
            Schema::dropIfExists('articles');
        }
    };

    „datenbank/factories/ArticleFactory.php„:

    <?php
    
    namespace DatabaseFactories;
    
    use Illuminate\Database\Eloquent\Factories\Factory;
    
    class ArticleFactory extends Factory
    {
    
        public function definition()
        {
            return [
                'title' => $this->faker->sentence(6),
                'excerpt' => $this->faker->paragraph(4),
                'body' => $this->faker->paragraph(15),
            ];
        }
    }

Das ist alles, was wir brauchen, um loszulegen! Jetzt können wir zur Sache kommen und Inertia.js in unser Projekt einführen.

Schritt 2: Inertia installieren

Der Installationsprozess von Inertia ist in zwei Hauptphasen unterteilt: serverseitig (Laravel) und clientseitig (VueJs).

Die offizielle Installationsanleitung in der Inertia-Dokumentation ist ein wenig veraltet, weil Laravel 9 jetzt standardmäßig Vite verwendet, aber auch das werden wir durchgehen.

1. Serverseitig

Als Erstes müssen wir die serverseitigen Adapter von Inertia mit dem folgenden Terminalbefehl über Composer installieren.

composer require inertiajs/inertia-laravel

Jetzt richten wir unser Root-Template ein. Das ist eine einzelne Blade-Datei, die zum Laden deiner CSS- und JS-Dateien verwendet wird, sowie ein Inertia-Root, das zum Starten unserer JavaScript-Anwendung verwendet wird.

Da wir die neueste Version von Laravel 9 v9.3.1 verwenden, müssen wir auch Vite aktivieren, indem wir es in unsere Tags in /resources/views/app.blade.php aufnehmen:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    
    <title inertia>{{ config('app.name', 'Laravel') }}</title>

    
    @vite('resources/js/app.js') @inertiaHead
  </head>

  <body class="font-sans antialiased">
    @inertia
  </body>
</html>

Beachte, wie wir den Projekttitel dynamisch abrufen können, indem wir das Attribut inertia zu den <title> Tags hinzufügen.

Außerdem haben wir die Direktive @vite in den Head eingefügt, um Vite den Pfad zu unserer JavaScript-Hauptdatei mitzuteilen, in der wir unsere App erstellt und unser CSS importiert haben. Vite ist ein Tool, das bei der JavaScript- und CSS-Entwicklung hilft, indem es Entwicklern ermöglicht, Frontend-Änderungen zu sehen, ohne die Seite während der lokalen Entwicklung aktualisieren zu müssen.

Als Nächstes erstellen wir die Middleware HandleInertiaRequests und veröffentlichen sie in unserem Projekt. Dazu rufen wir den folgenden Terminalbefehl im Stammverzeichnis unseres Projekts auf:

php artisan inertia:middleware

Sobald dies abgeschlossen ist, gehst du zu „App/Http/Kernel.php“ und registrierst HandleInertiaRequests als letztes Element in deinen Web-Middlewares:

'web' => [
    // ...
    App\Http\Middleware\HandleInertiaRequests::class,
],

2. Clientseitig

Als Nächstes müssen wir unsere Vue.js 3-Abhängigkeiten für das Frontend auf die gleiche Weise installieren wie auf der Serverseite:

npm install @inertiajs/inertia @inertiajs/inertia-vue3
// or
yarn add @inertiajs/inertia @inertiajs/inertia-vue3

Als Nächstes musst du Vue.js 3 einbinden:

npm install vue@next

Aktualisiere dann deine primäre JavaScript-Datei, um Inertia.js mit Vue.js 3, Vite und Laravel zu initialisieren:

„resources/js/app.js„:

import "./bootstrap";
import "../css/app.css";

import { createApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/inertia-vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) =>
    resolvePageComponent(
      `./Pages/${name}.vue`,
      import.meta.glob("./Pages/**/*.vue")
    ),
  setup({ el, app, props, plugin }) {
    return createApp({ render: () => h(app, props) })
      .use(plugin)
      .mount(el);
  },
});

Im obigen Codeschnipsel verwenden wir das Laravel-Plugin resolvePageComponent und weisen es an, unsere Komponenten aus dem Verzeichnis ./Pages/$name.vue aufzulösen. Der Grund dafür ist, dass wir unsere Inertia-Komponenten später in unserem Projekt in diesem Verzeichnis speichern werden und das Plugin uns dabei helfen wird, diese Komponenten automatisch aus dem richtigen Verzeichnis zu laden.

Jetzt müssen wir nur noch vitejs/plugin-vue installieren:

npm i @vitejs/plugin-vue

Und die Datei vite.config.js aktualisieren:

import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [
    laravel({
      input: ["resources/css/app.css", "resources/js/app.js"],
      refresh: true,
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false,
        },
      },
    }),
  ],
});

Der letzte Schritt besteht darin, unsere Abhängigkeiten zu installieren und unsere Dateien zu kompilieren:

npm install

npm run dev

Und voilà! Du hast jetzt eine funktionierende Laravel 9 Anwendung mit Vue.js 3 und Vite. Jetzt müssen wir nur noch sehen, was in Aktion passiert!

Inertia-Seiten erstellen

Erinnerst du dich an die beiden Blade-Dateien (index und show) für die Anzeige unserer Homepage und eines einzelnen Artikels?

Die einzige Blade-Datei, die wir bei der Verwendung von Inertia brauchen, ist app.blade.php, die wir schon einmal bei der Installation von Inertia verwendet haben. Was passiert jetzt mit diesen Dateien?

Wir werden diese Dateien von Blade-Komponenten in Inertia.js-Komponenten umwandeln.

Jede Seite in deiner Anwendung hat ihren eigenen Controller und ihre eigene JavaScript-Komponente mit Inertia. So kannst du nur die Daten abrufen, die für die jeweilige Seite benötigt werden, ohne eine API zu verwenden. Inertia-Seiten sind nichts anderes als JavaScript-Komponenten, in unserem Fall sind es Vue.js-Komponenten. Sie haben nichts besonders Bemerkenswertes an sich. Wir werden also alle HTML-Inhalte zwischen <template> Tags einpacken und alles, was mit JavaScript zu tun hat, wird mit <script> Tags eingepackt.

Erstelle einen Ordner namens „Seiten“ und verschiebe deine Dateien dorthin. Wir werden also „index.blade.php“ und „show.blade.php“ in „./resources/js/Pages“ ablegen. Dann ändern wir das Dateiformat in „.vue“ anstelle von „.blade.php“, wobei wir den ersten Buchstaben des Namens in Großbuchstaben schreiben und den Inhalt in eine Standard-Vue.js-Komponente verwandeln. Wir lassen die Tags <html>, <head> und <body> weg, da sie bereits in der Root-Blade-Komponente enthalten sind.

„resources/js/Pages/Index.vue„:

<script setup>
  //
</script>

<template>
  <header>
    <h1>Kinsta Blog</h1>
  </header>

  <main>
    <h2>Read our latest articles</h2>

    <section>
      <article>
        <div>
          <img src="/images/kinsta-logo.png" alt="Article thumbnail" />
        </div>

        <h3>Title for the blog</h3>
        <p>
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem
          itaque error vel perferendis aliquam numquam dignissimos, expedita
          perspiciatis consectetur!
        </p>

        <a href="#">Read more</a>
      </article>
    </section>
  </main>

  <footer>
    <h2>Join our Newsletter</h2>

    <input type="email" />
  </footer>
</template>

„resources/js/Seiten/Show.vue„:

<script setup>
  //
</script>

<template>
  <header>
    <h1>Welcome to Kinsta Blog</h1>
  </header>

  <main>
    <article>
      <h1>Title for the blog</h1>

      <p>Article content goes here</p>
    </article>
  </main>

  <footer>
    <h2>Join our Newsletter</h2>

    <input type="email" />
  </footer>
</template>

Da ist etwas, das mich wirklich stört! Wir kopieren und fügen unsere Kopf- und Fußzeile in jede Komponente ein, was keine gute Vorgehensweise ist. Lass uns ein Inertia-Basislayout erstellen, um unsere dauerhaften Komponenten zu speichern.

Erstelle einen Ordner namens „Layouts“ in „/resources/js“ und erstelle in diesem Ordner eine Datei namens „KinstaLayout.vue“. Diese Datei wird unsere Kopf- und Fußzeile und die main mit einer <slot /> enthalten, damit alle Komponenten, die mit diesem Layout verpackt sind, darin eingebettet werden können. Diese Datei sollte wie folgt aussehen:

„resources/js/Layouts/KinstaLayout.vue„:

<script setup></script>

<template>
    <header>
    <h1>Kinsta Blog</h1>
  </header>

  <main>
        <slot />
  </main>

  <footer>
    <h2>Join our Newsletter</h2>

    <input type="email" />
  </footer>

</template>

Dann werden wir dieses neue Layout in unsere Seiten importieren und den gesamten HTML-Inhalt damit umhüllen. Unsere Komponenten sollten wie folgt aussehen:

Index.vue:

<script setup>
import KinstaLayout from "../Layouts/KinstaLayout.vue";
</script>

<template>
  <KinstaLayout>
    <section>
      <h2>Read our latest articles</h2>
      <article>
        <div>
          <img src="/images/kinsta-logo.png" alt="Article thumbnail" />
        </div>

        <h3>Title for the blog</h3>
        <p>
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem
          itaque error vel perferendis aliquam numquam dignissimos, expedita
          perspiciatis consectetur!
        </p>

        <a href="#">Read more</a>
      </article>
    </section>
  </KinstaLayout>
 </template>

Show.vue:

<script setup>
 import KinstaLayout from "../Layouts/KinstaLayout.vue";
</script>

<template>
  <KinstaLayout>
    <article>
      <h1>Title for the blog</h1>

      <p>Article content goes here</p>
    </article>
  </KinstaLayout>
</template>

Laravel Routes und Inertia Rendering

Als Erstes verwenden wir die „ArticleFactory„-Datei, die wir aus unserem Tutorial kennen, und legen einige Artikel in unserer Datenbank an.

„database/seeders/databaseSeeder.php„:

<?php

namespace Database\Seeders;

use AppModelsArticle;
use IlluminateDatabaseSeeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        Article::factory(10)->create();
    }
}

Gib dann den folgenden Terminalbefehl ein, um deine Tabellen zu migrieren und die gefälschten Daten aus den Fabriken zu seeden:

php artisan migrate:fresh --seed

Dadurch werden 10 gefälschte Artikel in der Datenbank erstellt, die wir mithilfe des Laravel-Routings an unsere View übergeben müssen. Da wir nun Inertia zum Rendern von Views verwenden, wird sich die Art und Weise, wie wir unsere Routen schreiben, leicht ändern. Wir erstellen unsere erste Laravel-Inertia-Route in „routes/web.php“ und geben die Homepage-Ansicht aus „/resources/js/Pages/Index.vue“ zurück.

„routes/web.php„:

<?php

use App\Models\Article;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get('/', function () {
    return Inertia::render('Index', [
        'articles' => Article::latest()->get()
    ]);
})->name('home');

Beachte, dass wir Inertia importiert und nicht den view() Laravel-Helper verwendet haben, um die Ansicht zurückzugeben, sondern stattdessen Inertia::render. Inertia sucht auch standardmäßig nach dem Dateinamen, den wir in unserer Route im Ordner Pages unter „resources/js“ angegeben haben.

Gehe zur Indexdatei und setze die abgerufenen Daten als Prop und führe mit v-for eine Schleife darüber, um die Ergebnisse anzuzeigen. Definiere zwischen den Skript-Tags die übergebenen Daten als Requisite. Inertia muss nur wissen, welche Art von Daten du erwartest. In unserem Fall ist es ein „articles“-Objekt, das ein Array von Artikeln enthält.

„resources/js/Pages/Index.vue„:

<script setup>
import KinstaLayout from "../Layouts/KinstaLayout.vue";

  defineProps({
    articles: Object,
  });
</script>

Beachte, dass es ausreicht, ihn nur als Prop zu definieren, ohne ihn zurückzugeben, weil wir das setup Format für die Vue.js 3 Composition API verwenden. Wenn wir die Options-API verwenden würden, müssten wir sie zurückgeben.

Lass uns die Schleife erstellen:

<template>
  <KinstaLayout>
    <h2>Read our latest articles</h2>

    <section>
      // Looping over articles
      <article v-for="article in articles":key="article.id">
        <div>
          <img src="/images/kinsta-logo.png" alt="Article thumbnail" />
        </div>

        <h3>{{article.title}}</h3>
        <p>{{article.excerpt}}</p>

        <a href="#">Read more</a>
      </article>
    </section>
  </KinstaLayout>
</template>

npm run dev (lass es laufen, weil wir Vite verwenden) und php artisan serve, um den Laravel-Entwicklungsserver zu starten und auf unsere Website zuzugreifen, wir sehen die erwartete Seite, die alle zehn Artikel in der Datenbank anzeigt.

Jetzt verwenden wir die Vue DevTools-Erweiterung von Google Chrome, mit der wir meine Anwendung debuggen können. Wir wollen dir zeigen, wie unsere Daten an die Komponente übergeben werden.

Die Vue DevTools-Erweiterung von Chrome zeigt eine Liste von Inertia-Eigenschaften für die geöffnete Seite
Prüfen der Eigenschaften von Inertia

„Articles“ wird der Komponente als prop-Objekt übergeben, das ein Array von Artikeln enthält. Jeder Artikel im Array ist auch ein Objekt mit Eigenschaften, die den Daten entsprechen, die es aus der Datenbank bezogen hat. Das bedeutet, dass alle Daten, die wir von Laravel zu Inertia übertragen, als Prop behandelt werden.

Verwendung von Tailwind CSS mit Inertia.js

Da Tailwind bereits in unserem Projekt installiert ist, müssen wir ihm nur noch sagen, dass es unsere Inertia-Komponenten lesen soll. Dazu müssen wir die Datei „tailwind.config.js“ wie folgt bearbeiten:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./storage/framework/views/*.php",
    "./resources/views/**/*.blade.php",
    "./resources/js/**/*.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Stelle dann sicher, dass wir unsere CSS-Datei in „resources/js/app.js“ importiert haben:

import "../css/app.css";

Jetzt sind wir bereit, unsere Komponenten zu gestalten.

„resources/js/Pages/Index.vue„:

<script setup>
import KinstaLayout from "../Layouts/KinstaLayout.vue";

  defineProps({
    articles: Object,
  });
</script>

<template>
 <KinstaLayout>
    <h2 class="text-2xl font-bold py-10">Read our latest articles</h2>

    <section class="space-y-5 border-b-2 pb-10">
      <article
        v-for="article in articles"
        :key="article.id"
        class="flex justify-center items-center shadow-md bg-white rounded-xl p-4 mx-auto max-w-3xl"
      >

         <img
            src="/images/kinsta-logo.png"
            class="w-32 h-32 rounded-xl object-cover"
            alt=""
         />

        <div class="flex flex-col text-left justify-between pl-3 space-y-5">
          <h3
            class="text-xl font-semibold text-indigo-600 hover:text-indigo-800"
          >
            <a href="#">{{ article.title }}</a>
          </h3>
          <p>
           {{ article.excerpt }}
          </p>
          <a
            href="#"
            class="text-indigo-600 hover:text-indigo-800 w-fit self-end font-semibold"
            >Read more</a
          >
        </div>
      </article>
    </section>
 </KinstaLayout>
</template>

„resources/js/Layouts/KinstaLayout.vue„:

<script setup></script>

<template>
    <header
        class="bg-gradient-to-r from-blue-700 via-indigo-700 to-blue-700 w-full text-center py-4"
    >
        <h1 class="text-white font-bold text-4xl">Kinsta Blog</h1>
    </header>

    <main class="container mx-auto text-center">
        <slot />
    </main>

    <footer
        class="bg-gradient-to-b from-transparent to-gray-300 w-full text-center mt-5 py-10 mx-auto"
    >
        <h2 class="font-bold text-xl pb-5">Join our Newsletter</h2>

        <input
            class="rounded-xl w-80 h-12 px-3 py-2 shadow-md"
            type="email"
            placeholder="Write your email.."
        />
    </footer>
</template>

Wenn du dir den Browser ansiehst, wirst du feststellen, dass Vite die Seite bereits mit der Tailwind-Magie aktualisiert hat.

Ein scrollendes Bild, das eine funktionierende Version des "Kinsta Blog"-Beispiels von vorhin zeigt
Rendering der Inertia-Eigenschaften

Inertia-Links

Da wir nun eine funktionierende Homepage haben, die alle Artikel in der Datenbank anzeigen kann, müssen wir eine weitere Route erstellen, um einzelne Artikel anzuzeigen. Erstellen wir eine neue Route und setzen wir die URL auf einen „id“-Platzhalter:

„routes/web.php

<?php

use App\Models\Article;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get('/', function () {
    return Inertia::render('Index', [
        'articles' => Article::latest()->get()
    ]);
})->name('home');

Route::get('/posts/{article:id}', function (Article $article) {
    return Inertia::render('Show', [
        'article' => $article
    ]);
})->name('article.show');

Wir haben das Modell „Artikel“ importiert und eine neue Route hinzugefügt, die die Show.vue Inertia-Komponente zurückgibt. Außerdem haben wir die Laravel-Routenmodellbindung genutzt, mit der Laravel automatisch den Artikel abrufen kann, auf den wir uns beziehen.

Jetzt brauchen wir nur noch eine Möglichkeit, diese Route zu besuchen, indem wir auf einen Link auf der Homepage klicken, ohne die gesamte Seite neu laden zu müssen. Das ist mit dem magischen Tool von Inertia <Link> möglich. In der Einleitung haben wir bereits erwähnt, dass Inertia <Link> als Wrapper für ein Standard-Anker-Tag <a> verwendet und dass dieser Wrapper dafür gedacht ist, den Seitenbesuch so nahtlos wie möglich zu gestalten. In Inertia kann sich das <Link> Tag als Anker-Tag verhalten, das <GET> Anfragen ausführt, aber es kann auch gleichzeitig als <button> und <form> fungieren. Schauen wir uns an, wie wir es in unserem Projekt anwenden können.

In unserer Index.vue importieren wir <Link> von Inertia und entfernen die Anker-Tags <a> und ersetzen sie durch Inertia-Tags <Link>. Das Attribut href wird auf die URL der Route gesetzt, die wir zuvor für die Anzeige des Artikels erstellt haben:

<script setup>
import KinstaLayout from "../Layouts/KinstaLayout.vue";
import { Link } from "@inertiajs/inertia-vue3";

defineProps({
    articles: Object,
});
</script>

<template>
    <KinstaLayout>
        <section class="space-y-5 border-b-2 pb-10">
            <h2 class="text-2xl font-bold pt-10 mx-auto text-center">
                Read our latest articles
            </h2>

            <article
                v-for="article in articles"
                :key="article.id"
                class="flex justify-center items-center shadow-md bg-white rounded-xl p-4 mx-auto max-w-3xl"
            >
                <img
                    src="/images/kinsta-logo.png"
                    class="w-32 h-32 rounded-xl object-cover"
                    alt=""
                />

                <div
                    class="flex flex-col text-left justify-between pl-3 space-y-5"
                >
                    <h3
                        class="text-xl font-semibold text-indigo-600 hover:text-indigo-800"
                    >
                        <Link :href="'/posts/' + article.id">{{
                            article.title
                        }}</Link>
                    </h3>
                    <p>
                        {{ article.excerpt }}
                    </p>
                    <Link
                        :href="'/posts/' + article.id"
                        class="text-indigo-600 hover:text-indigo-800 w-fit self-end font-semibold"
                        >Read more
                    </Link>
                </div>
            </article>
        </section>
    </KinstaLayout>
</template>

Lass uns Show.vue mit Tailwind stylen, damit es ein bisschen schicker aussieht und für unseren Besuch bereit ist. Außerdem müssen wir dem Programm mitteilen, dass es ein „Artikel“-Objekt erwarten und es als Requisite setzen soll:

<script setup>
import KinstaLayout from "../Layouts/KinstaLayout.vue";

defineProps({
    article: Object,
});
</script>

<template>
    <KinstaLayout>
        <article class="mx-auto mt-10 flex justify-center max-w-5xl border-b-2">
            <img
                src="/images/kinsta-logo.png"
                class="w-80 h-80 rounded-xl mx-auto py-5"
                alt=""
            />
            <div class="text-left flex flex-col pt-5 pb-10 px-10">
                <h1 class="text-xl font-semibold mb-10">{{ article.title }}</h1>
                <p>{{ article.body }}</p>
            </div>
        </article>
    </KinstaLayout>
</template>

Wenn wir nun auf den Artikeltitel oder „Weiterlesen“ klicken, werden wir auf magische Weise zu Show.vue transportiert, ohne dass die Seite aktualisiert werden muss.

Die Beispielseite "Kinsta Blog" zeigt Artikelkarten mit funktionierenden Links
Inertia-Links im Einsatz

In unserem Fall verwenden wir <Link> als Anker-Tag, das eine GET Anfrage an die Route sendet und die neuen Daten zurückgibt, aber wir können <Link> auch für POST, PUT, PATCH und DELETE

„routes/web.php„:

<Link href="/logout" method="post" as="button" type="button">Logout</Link>

Laravel Inertia Tipps und Tricks, die du kennen solltest

Wir haben jetzt eine funktionierende SPA mit Laravel, Inertia und Tailwind CSS gebaut. Aber mit Inertia können wir noch viel mehr erreichen. Es ist an der Zeit, sich einige Inertia-Techniken anzueignen, die sowohl den Entwicklern als auch den Besuchern der Anwendung helfen werden.

URLs generieren

Du hast vielleicht bemerkt, dass wir unseren Laravel-Routen Namen hinzugefügt haben, ohne sie zu benutzen. Mit Inertia können wir unsere benannten Routen in unseren Komponenten verwenden, anstatt die vollständige Route manuell aufzuschreiben.

Wir können dies erreichen, indem wir das Ziggy-Paket in unserem Projekt installieren:

composer require tightenco/ziggy

Gehe dann zu „resources/js/app.js“ und aktualisiere es wie folgt:

import "./bootstrap";
import "../css/app.css";

import { createApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/inertia-vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m";

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) =>
        resolvePageComponent(
            `./Pages/${name}.vue`,
            import.meta.glob("./Pages/**/*.vue")
        ),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            .mount(el);
    },
});

Gehe zu „/resources/views/app.blade.php“ und aktualisiere den head mit der Direktive @routes:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    
    <title inertia>{{ config('app.name', 'Laravel') }}</title>

    
    @routes
    @vite('resources/js/app.js')
    @inertiaHead
</head>

<body class="font-sans antialiased">
    @inertia
</body>

</html>

…und aktualisiere deine NPM-Pakete, indem du die beiden folgenden Terminalbefehle ausführst:

npm install && npm run dev

Mit diesem Paket können wir benannte Routen in unseren Inertia-Komponenten verwenden. Also gehen wir zu Index.vue und entfernen die alte manuelle Route und ersetzen sie durch den Routennamen, während wir die Daten ganz normal wie in unserem Controller übergeben.

Wir werden dies ersetzen:

<Link :href="'/posts/' + article.id">
   {{ article.title }}
</Link>

…durch dies:

<Link :href="route('article.show', article.id)">
   {{ article.title }}
</Link>

Damit erhalten wir genau das gleiche Verhalten wie vorher, aber es ist entwicklerfreundlicher und extrem hilfreich, wenn deine Route viele Parameter erwartet.

Fortschrittsindikatoren

Dies ist eine der schönsten Funktionen von Inertia.js. Da SPA ein interaktives Nutzererlebnis bietet, wäre eine ständige Rückmeldung, ob eine Anfrage geladen wird, eine fantastische Ergänzung der Anwendung. Dies kann durch eine separate Bibliothek erreicht werden, die Inertia anbietet.

Die Bibliothek „@inertiajs/progress“ ist ein Wrapper um NProgress, der die Ladeanzeigen abhängig von Inertia-Ereignissen anzeigt. Du musst nicht wirklich wissen, wie das hinter den Kulissen funktioniert, also lass es uns einfach zum Laufen bringen.

Wir können diese Bibliothek mit dem folgenden Terminalbefehl installieren:

npm install @inertiajs/progress

Sobald sie installiert ist, müssen wir sie in „resources/js/app.js“ importieren:

import "./bootstrap";
import "../css/app.css";

import { createApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/inertia-vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m";
import { InertiaProgress } from "@inertiajs/progress";

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) =>
        resolvePageComponent(
            `./Pages/${name}.vue`,
            import.meta.glob("./Pages/**/*.vue")
        ),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            .mount(el);
    },
});

InertiaProgress.init({ color: "#000000", showSpinner: true });

Dies zeigt einen Ladebalken und einen Lade-Spinner in schwarzer Farbe an, aber wir können die Farbe zusammen mit anderen hilfreichen Optionen ändern, die du in der Inertia.js-Dokumentation zum Fortschrittsindikator findest.

Die blaue Kopfzeile "Kinsta Blog" mit dem sich drehenden Indikator oben rechts
Der Inertia-Fortschrittsindikator (oben rechts).

Scroll-Management

In manchen Fällen möchtest du vielleicht zu einer neuen Seite navigieren und dabei die gleiche Bildlaufposition beibehalten. Das ist z. B. notwendig, wenn du Nutzern die Möglichkeit gibst, Kommentare zu hinterlassen; dabei wird ein Formular abgeschickt und der neue Kommentar aus der Datenbank in deine Komponente geladen; du möchtest, dass dies geschieht, ohne dass der Nutzer seine Scrollposition verliert. Inertia übernimmt diese Aufgabe für uns.

In unserem Fall wenden wir dies auf unseren <Link> Tag in Index.vue an. Um die Scrollposition beizubehalten, während wir mit Inertia’s <Link> zu einer anderen Seite weiterleiten, müssen wir nur das Attribut preserve-scroll zum <Link> hinzufügen:

<Link :href="route('article.show', article.id)" preserve-scroll>
  {{ article.title }}
</Link>

SEO-Tipps

Seit es SPAs gibt, machen sich die Menschen Gedanken über die Suchmaschinenoptimierung (SEO). Es ist allgemein bekannt, dass Suchmaschinen eine Webanwendung nur schwer crawlen können, weil alles auf der Client-Seite gerendert wird, was dazu führt, dass deine Website nicht ganz oben in den Suchergebnissen auftaucht. Aber wie kommt es dann, dass beliebte Plattformen wie Facebook und Github jetzt SPAs sind und trotzdem gut in der SEO abschneiden?

Nun, das ist keine unmögliche Mission mehr. Inertia bietet ein paar Lösungen an, die deiner SPA helfen, SEO-freundlich zu werden.

Inertia Vue SSR mit Laravel und Vite

Suchmaschinen suchen immer nach HTML auf deiner Website, um den Inhalt zu identifizieren; wenn du jedoch kein HTML in deinen URLs hast, wird diese Aufgabe schwieriger. Wenn du SPAs entwickelst, hast du nur JavaScript und JSON auf deiner Seite. Inertia hat ein Server-Side Rendering (SSR) Feature eingeführt, das du in deine Anwendung einbauen kannst. Damit kann deine Anwendung einen ersten Seitenbesuch auf dem Server vorrendern und dann das gerenderte HTML an den Browser senden. Auf diese Weise können die Nutzer/innen deine Seiten sehen und mit ihnen interagieren, bevor sie vollständig geladen sind, und es hat auch andere Vorteile, wie z. B. die Verkürzung der Zeit, die Suchmaschinen brauchen, um deine Website zu indizieren.

Um die Funktionsweise zusammenzufassen: Inertia erkennt, ob es auf einem Node.js-Server läuft und rendert die Komponentennamen, die Eigenschaften, die URL und die Asset-Version in HTML. Auf diese Weise können sowohl der Nutzer als auch die Suchmaschine praktisch alles sehen, was deine Seite zu bieten hat.

Da wir es aber mit Laravel zu tun haben, macht das wenig Sinn, denn Laravel ist ein PHP-Framework und läuft nicht auf einem Node.js-Server. Deshalb werden wir die Anfrage an einen Node.js-Dienst weiterleiten, der die Seite rendert und HTML zurückgibt. Dadurch wird unsere Laravel-Vue-Anwendung standardmäßig SEO-freundlich.

Zuerst müssen wir das Vue.js SSR npm-Paket installieren:

npm install @vue/server-renderer

Ein weiteres hilfreiches „NPM“-Paket von Inertia bietet einen einfachen „HTTP“-Server. Es wird dringend empfohlen, dass du es installierst:

npm install @inertiajs/server

Dann fügen wir in „resources/js/“ eine neue Datei namens ssr.js hinzu. Diese Datei ist der app.js-Datei, die wir bei der Installation von Inertia erstellt haben, sehr ähnlich, nur dass sie in Node.js und nicht im Browser ausgeführt wird:

import { createSSRApp, h } from "vue";
import { renderToString } from "@vue/server-renderer";
import { createInertiaApp } from "@inertiajs/inertia-vue3";
import createServer from "@inertiajs/server";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m";

const appName = "Laravel";

createServer((page) =>
    createInertiaApp({
        page,
        render: renderToString,
        title: (title) => `${title} - ${appName}`,
        resolve: (name) =>
            resolvePageComponent(
                `./Pages/${name}.vue`,
                import.meta.glob("./Pages/**/*.vue")
            ),
        setup({ app, props, plugin }) {
            return createSSRApp({ render: () => h(app, props) })
                .use(plugin)
                .use(ZiggyVue, {
                    ...page.props.ziggy,
                    location: new URL(page.props.ziggy.location),
                });
        },
    })
);

Achte darauf, dass du nicht alles in die Datei ssr.js einfügst, da sie für Besucher/innen nicht sichtbar sein wird. Diese Datei ist nur für Suchmaschinen und Browser gedacht, um die Daten auf deiner Seite anzuzeigen, also füge nur das ein, was für deine Daten wichtig ist oder deine Daten verfügbar macht.

„Standardmäßig arbeitet der SSR-Server von Inertia auf Port 13714. Du kannst dies jedoch ändern, indem du der Methode createServer ein zweites Argument mitgibst.“ Inertia DOCss.

In den Inertia.js DOCs wird nicht erklärt, wie man Inertia SSR in Vite integriert, aber wir werden das jetzt nachholen. Gehe zu vite.config.js und füge den folgenden Text ein:

import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
    plugins: [
        laravel({
            input: "resources/js/app.js",
            ssr: "resources/js/ssr.js",
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
    ],
});

Als Nächstes gehst du zur package.json und änderst das Build-Skript:

"build": "vite build && vite build --ssr"

Wenn wir nun npm run build ausführen, wird Vite unser SSR-Bündel für die Produktion bauen. Weitere Informationen dazu findest du unter Inertia SSR DOCs und Vite SSR DOCs.

Titel und Meta

Da JavaScript-Anwendungen innerhalb des Dokuments <body> gerendert werden, können sie kein Markup für das Dokument <head> rendern, da es außerhalb ihres Bereichs liegt. Inertia hat eine <Head> Komponente, die verwendet werden kann, um die <title>, <meta> Tags und andere <head> Komponenten zu setzen.

Um das Element <head> zu deiner Seite hinzuzufügen, müssen wir <Head> aus Inertia importieren, genauso wie wir es mit der Komponente <Link> gemacht haben:

import { Head } from '@inertiajs/inertia-vue3'

<Head>
  <title>Kinsta Blog</title>
  <meta name="description" content="Kinsta blog for developers">
</Head>

Wir können auch einen globalen Titel für alle Seiten hinzufügen, damit der Name deiner Anwendung neben dem Titel auf jeder Seite erscheint. Das haben wir bereits in der Datei app.js getan:

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    //
});

Wenn wir also <Head title="Home"> auf der Startseite unserer Anwendung mit einem Titel versehen, wird dieser wie folgt dargestellt: <title>Home - My App</title>.

Überwachung deiner App

Die Geschwindigkeit ist einer der wichtigsten Faktoren für die Optimierung der SEO-Leistung deiner Website. Wenn du WordPress für deine Website verwendest, hilft dir Kinsta APM dabei, deine Anwendung in Aktion zu überwachen und im Auge zu behalten. Es hilft dir, WordPress-Performance-Probleme zu erkennen und ist für alle von Kinsta gehosteten Websites kostenlos verfügbar.

Zusammenfassung

Inertia.js ist eine der wichtigsten Technologien auf dem Markt. Kombiniere sie mit Laravel und du hast eine moderne Single-Page-Anwendung, die mit PHP und JavaScript erstellt wurde. Taylor Otwell, der Schöpfer von Laravel, ist so sehr an Inertia interessiert, dass Laravel seine beliebtesten Starterkits, Laravel Breeze und Jetstream, mit Inertia- und SSR-Unterstützung herausgebracht hat.

Wenn du ein Laravel-Fan oder ein professioneller Entwickler bist, wird dir Inertia.js zweifellos ins Auge fallen. In diesem Tutorial haben wir in nur wenigen Minuten einen sehr einfachen Blog erstellt. Es gibt noch viel über Inertia zu lernen, und dies wird vielleicht nur der erste von vielen Artikeln und Tutorials sein.

Was möchtest du noch über Laravel erfahren? Lass es uns im Kommentarbereich unten wissen.

Mostafa Said

Ich bin Mostafa, ein Full-Stack-Entwickler mit einer Vorliebe für Laravel, Inertia und JavaScript-Frameworks. Wenn ich nicht gerade programmiere, gebe ich mein Wissen in Tutorials weiter, nehme an Hackathons teil (und gewinne ein paar) und verbreite meine Liebe zur Technik, indem ich das Gelernte weiterleite.