Le applicazioni multi-pagina (MPA) stanno diventando sempre meno popolari. Piattaforme famose come Facebook, Twitter, YouTube, Github e molte altre usano già la tecnologia delle applicazioni a pagina singola (SPA).

Questa tecnologia di tendenza permette agli utenti di interagire con le applicazioni web in modo rapido e reattivo perché tutto è renderizzato lato client. Tuttavia, può essere un problema per chi sviluppa applicazioni renderizzate lato server con framework come Laravel o Django.

Per fortuna Inertia.js è intervenuto in soccorso.

In questo articolo vi mostreremo come iniziare a usare Inertia.js e come utilizzarlo con Laravel, Vue.js e Tailwind CSS per creare una moderna applicazione web per blog. Vedremo anche come rendere le SPA più SEO-friendly e qualche altro trucco.

Se state iniziando a lavorare con Laravel, vi consigliamo di leggere prima questo articolo per essere pronti a partire.

Perché una SPA?

Prima di chiederci perché dovremmo usare Inertia, dobbiamo prima chiederci: “Perché una SPA?”

Perché qualcuno dovrebbe preferire le applicazioni renderizzate lato client alle tradizionali applicazioni lato server? Cosa spinge sviluppatrici e sviluppatori Laravel full-stack a dire addio ai componenti blade?

La risposta è breve: perché la velocità e la reattività sono essenziali per il successo di un’applicazione.

Nel caso delle AMP, il browser invia costantemente richieste al backend, che esegue numerose query al database. Dopo che il database e il server elaborano le query e le trasmettono al browser, la pagina viene renderizzata.

Le SPA sono invece diverse. L’applicazione porta tutto ciò di cui l’utente ha bisogno direttamente nella pagina, eliminando la necessità per il browser di inviare query o ricaricare la pagina per rendere nuovi elementi HTML.

Grazie a questa esperienza unica per l’utente, molte grandi aziende chiedono a gran voce che i loro siti web diventino applicazioni a pagina singola.

Detto questo, la creazione di un’applicazione a pagina singola può essere difficile per chi sviluppa con Laravel perché richiederebbe l’utilizzo di Vue.js o React al posto dei template blade, con la conseguente perdita di molte gemme di Laravel che fanno risparmiare tempo e fatica.

Ora che abbiamo Inertia.js, però, tutto cambia.

Perché Inertia?

Se prima di Inertia gli sviluppatori Laravel costruivano web SPA con Vue, dovevano impostare API e restituire dati JSON con Laravel, quindi usare qualcosa come AXIOS per recuperare i dati nei componenti Vue. Avrebbero inoltre bisogno di qualcosa come Vue Router per gestire i percorsi, il che significherebbe perdere il routing di Laravel, così come i middleware e i controller.

Inertia.js, invece, permette di costruire moderne app a pagina singola in Vue, React e Svelte utilizzando il classico routing e i controller lato server. Inertia è stato progettato per chi sviluppa con Laravel, Ruby on Rails e Django per consentire loro di creare applicazioni senza modificare le tecniche di programmazione che prevedono la creazione di controller, il recupero dei dati da un database e il rendering delle viste

Grazie a Inertia.js, sviluppatrici e sviluppatori Laravel si sentiranno a casa.

Come Funziona Inertia

Costruire SPA solo con Laravel e Vue vi darà una pagina JavaScript completa per il frontend, ma non vi fornirà un’esperienza di app a pagina singola. Ogni link cliccato causerà il riavvio del framework lato client al successivo caricamento della pagina.

È qui che entra in gioco Inertia.

Inertia è fondamentalmente una libreria di routing lato client. Vi permette di navigare tra le pagine senza dover ricaricare l’intera pagina. Questo avviene tramite il componente <Link>, che è un involucro leggero attorno a un tag di ancoraggio standard.

Quando fate clic su un link, Inertia intercetta il clic e vi reindirizza a XHR. In questo modo il browser non ricaricherà la pagina, offrendo all’utente un’esperienza completa di una sola pagina.

Come Iniziare a Usare Inertia

Una pagina semplice con
Una pagina di esempio realizzata con Inertia.js

Per capire Inertia e come integrarlo con Laravel, costruiremo una web app per blog chiamata Kinsta Blog utilizzando la combinazione più potente: Laravel per il backend, Vue.js per il frontend JavaScript e Tailwind CSS per lo styling.

Se preferite seguire questo tutorial in un ambiente locale, potete usare DevKinsta, un potente strumento per developer, designer e agenzie che consente di creare applicazioni web WordPress a una o più pagine. Fortunatamente, WordPress può essere facilmente integrato con Laravel grazie al pacchetto Corcel.

Prerequisiti

Per trarre il massimo da questo tutorial, sarebbe meglio avere:

  • Nozioni di base di Laravel (installazione, database, migrazioni di database, Eloquent Models, controller e routing)
  • Nozioni di base di Vue.js (installazione, struttura e moduli)

Se non siete sicuri di avere tutti i requisiti, date un’occhiata a questi fantastici tutorial su Laravel gratuiti e a pagamento. Ora iniziamo.

Passo 1: Installare gli Elementi Principali

Per concentrarvi su Inertia.js e passare subito alla parte più divertente, verificate di avere la seguente configurazione pronta per l’uso:

  1. Un progetto Laravel 9 appena installato e denominato kinsta-blog
  2. Tailwind CSS CLI installato nel nostro progetto Laravel
  3. File immagine “kinsta-logo.png”. Scaricare e decomprimere il pacchetto di loghi Kinsta da https://kinsta.com/press/ e copiare kinsta-logo2.png nella directory public/images come kinsta-logo.png.
  4. Due componenti blade in kinsta-blog/resources/views per visualizzare la homepage del blog e un singolo articolo del blog come mostrato di seguito: “/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. Database locale MySQL chiamato kinsta_blog collegato al nostro progetto:”.env“:
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=kinsta_blog
    DB_USERNAME=root
    DB_PASSWORD=
  6. Modello di articolo, migrazioni e factory: “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');
    	}
    };

    “database/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),
        	];
    	}
    }

Questo è tutto ciò che ci serve per iniziare! Ora passiamo al sodo e introduciamo Inertia.js nel nostro progetto.

Passo 2: Installare Inertia

Il processo di installazione di Inertia si divide in due fasi principali: lato server (Laravel) e lato client (VueJs).

La guida ufficiale all’installazione contenuta nella documentazione di Inertia è un po’ obsoleta perché Laravel 9 ora usa Vite per impostazione predefinita, ma ci occuperemo anche di questo.

1. Lato Server

La prima cosa da fare è installare gli adattatori lato server di Inertia con il seguente comando da terminale tramite Composer.

composer require inertiajs/inertia-laravel

Ora configureremo il nostro modello di root, che sarà un singolo file blade che verrà utilizzato per caricare i file CSS e JS, oltre a una root Inertia che verrà utilizzata per lanciare la nostra applicazione JavaScript.

Poiché stiamo utilizzando la versione più recente di Laravel 9 v9.3.1, dobbiamo anche consentire a Vite di fare la sua magia includendolo nei nostri tag in /resources/views/app.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 inertia>{{ config('app.name', 'Laravel') }}</title>

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

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

Notate come siamo in grado di recuperare il titolo del progetto in modo dinamico aggiungendo l’attributo inertia ai tag <title>.

Abbiamo anche aggiunto la direttiva @vite all’head per far conoscere a Vite il percorso del nostro file principale JavaScript in cui abbiamo creato la nostra applicazione e importato il nostro CSS. Vite è uno strumento che aiuta lo sviluppo di JavaScript e CSS consentendo di visualizzare le modifiche del frontend senza dover aggiornare la pagina durante lo sviluppo locale.

La nostra prossima mossa sarà creare il middleware HandleInertiaRequests e pubblicarlo nel nostro progetto. Possiamo farlo lanciando il seguente comando da terminale nella directory principale del nostro progetto:

php artisan inertia:middleware

Una volta completata questa operazione, andate in “App/Http/Kernel.php” e registrate HandleInertiaRequests come ultimo elemento dei middleware web:

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

2. Lato Client

Successivamente, dobbiamo installare le dipendenze di Vue.js 3 per il frontend così come abbiamo fatto lato server:

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

Successivamente, dovrete inserire Vue.js 3:

npm install vue@next

Quindi aggiornate il vostro file JavaScript principale per inizializzare Inertia.js con Vue.js 3, Vite e Laravel:

“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);
  },
});

Nel codice che abbiamo riportato sopra, usiamo il plugin di Laravel resolvePageComponent e gli diciamo di risolvere i nostri componenti dalla directory ./Pages/$name.vue. Questo perché salveremo i nostri componenti Inertia in questa directory più avanti nel nostro progetto e il plugin ci aiuterà a caricare automaticamente i componenti dalla directory corretta.

Non resta che installare vitejs/plugin-vue:

npm i @vitejs/plugin-vue

E aggiornare il file vite.config.js:

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,
    	},
  	},
	}),
  ],
});

Il passo finale consiste nell’installare le dipendenze e compilare i file:

npm install

npm run dev

E voilà! Avete ottenuto un’applicazione Laravel 9 funzionante con Vue.js 3 e Vite. Ora dobbiamo vedere qualcosa in azione!

Creare Pagine con Inertia

Vi ricordate i due file blade (index e show) per visualizzare la nostra homepage e un singolo articolo?

L’unico file blade di cui avremo bisogno durante l’utilizzo di Inertia è app.blade.php, che abbiamo già usato una volta durante l’installazione di Inertia. Cosa succede ora a questi file?

Trasformeremo questi file da componenti blade a componenti Inertia.js.

Ogni pagina della vostra applicazione ha il suo controller e il suo componente JavaScript con Inertia. Questo vi permette di ottenere solo i dati necessari per quella pagina, senza usare un’API. Le pagine Inertia non sono altro che componenti JavaScript, nel nostro caso sono componenti Vue.js. Non hanno nulla di particolarmente degno di nota. Per questo motivo, avvolgeremo tutti i contenuti HTML tra i tag <template> e tutto ciò che riguarda JavaScript sarà avvolto dai tag <script>.

Create una cartella chiamata “Pages” e spostate lì i vostri file. Quindi avremo “index.blade.php” e “show.blade.php” in “./resources/js/Pages“. Poi modificheremo il formato del file in “.vue” invece di “.blade.php”, rendendo la prima lettera del nome maiuscola e trasformando il contenuto in un componente standard di Vue.js. Escluderemo i tag <html>, <head> e <body> perché sono già inclusi nel componente principale di blade.

“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/Pages/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>

C’è qualcosa che ci preoccupa molto! Continuiamo a copiare e incollare l’intestazione e il footer in ogni componente, il che non è una buona pratica. Creiamo un layout di base Inertia per memorizzare i nostri componenti persistenti.

Create una cartella chiamata “Layouts” in “/resources/js” e all’interno della cartella create un file chiamato “KinstaLayout.vue”. Questo file conterrà la nostra intestazione e il footer e il sito main con un <slot /> per consentire a tutti i componenti che sono stati inseriti in questo layout di essere incorporati al suo interno. Questo file dovrebbe avere il seguente aspetto:

“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>

Quindi importeremo questo nuovo layout nelle nostre pagine e avvolgeremo tutti i contenuti HTML con esso. I nostri componenti dovrebbero avere questo aspetto:

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>

Percorsi Laravel e Render Inertia

Per prima cosa usiamo il file “ArticleFactory” che abbiamo ottenuto come punto di partenza del nostro tutorial e inseriamo alcuni articoli nel nostro database.

“database/seeders/databaseSeeder.php“:

<?php

namespace Database\Seeders;

use App\Models\Article;
use Illuminate\Database\Seeder;

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

Quindi eseguite il seguente comando da terminale per migrare le vostre tabelle e seminare i dati simulati dalle factory:

php artisan migrate:fresh --seed

In questo modo verranno creati 10 finti articoli nel database, che dovremo passare alla nostra vista utilizzando il routing di Laravel. Ora che usiamo Inertia per il rendering delle viste, il modo in cui scrivevamo i nostri percorsi cambierà leggermente. Creiamo il nostro primo percorso Laravel Inertia in “routes/web.php” e restituiamo la vista homepage da “/resources/js/Pages/Index.vue“.

“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');

Abbiamo importato Inertia e non abbiamo utilizzato l’helper view() di Laravel per restituire la vista, ma abbiamo invece usato Inertia::render. Inoltre, per impostazione predefinita, Inertia cercherà il nome del file che abbiamo indicato nel nostro percorso nella cartella Pages in “resources/js”.

Andate al file Index, impostate i dati recuperati come prop e fate un ciclo su di essi con v-for per mostrare i risultati. Tra i tag dello script, definite i dati passati come prop. Tutto ciò che Inertia deve sapere è il tipo di dati che vi aspettate, che nel nostro caso è un oggetto “Articolo” contenente un array di articoli.

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

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

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

Notate che è sufficiente definirlo come oggetto senza restituirlo perché stiamo usando il formato setup per l’API di composizione di Vue.js 3. Se stiamo usando l’API di composizione di Vue.js 3, allora è necessario definire il tipo di oggetto che ci aspettiamo. Se usassimo l’API delle opzioni, dovremmo restituirla.

Creiamo il loop:

<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 (lasciandolo in esecuzione perché stiamo usando Vite) e php artisan serve per avviare il server di sviluppo laravel e accedere al nostro sito web, vedremo la pagina prevista che mostra tutti i dieci articoli presenti nel database.

Ora stiamo usando l’estensione Vue DevTools di Google Chrome, che ci permette di eseguire il debug della nostra applicazione. Vediamo come vengono passati i dati al componente.

L'estensione Vue DevTools di Chrome mostra l'elenco delle proprietà di Inertia per la pagina aperta.
Ispezione delle proprietà di Inertia.

“articles” viene passato al componente come un oggetto prop contenente un array di articoli; ogni articolo dell’array è anche un oggetto con proprietà che corrispondono ai dati acquisiti dal database. Questo significa che tutti i dati che trasferiamo da Laravel a Inertia saranno trattati come oggetti prop.

Usare il CSS di Tailwind con Inertia.js

Dal momento che Tailwind è già installato nel nostro progetto al punto di partenza, tutto ciò che dobbiamo fare è dirgli di leggere i nostri componenti Inertia. Questo può essere fatto modificando “tailwind.config.js” come segue:

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

Verificate poi di aver importato il nostro file CSS in “resources/js/app.js“:

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

E ora siamo pronti a creare lo stile dei nostri componenti.

“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>

Se guardate il browser, noterete che Vite ha già aggiornato la pagina con la magia di Tailwind.

Un'immagine a scorrimento che mostra una versione funzionante del
Rendering delle proprietà di Inertia.

Link di Inertia

Ora che abbiamo una homepage funzionante in grado di visualizzare tutti gli articoli del database, dobbiamo creare un altro percorso per visualizzare i singoli articoli. Creiamo un nuovo percorso e impostiamo l’URL con un carattere jolly “id”:

“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');

Abbiamo importato il modello “Articolo” e aggiunto una nuova rotta per restituire il componente Show.vue Inertia. Abbiamo anche sfruttato il binding del modello di percorso di Laravel, che permette a Laravel di ottenere automaticamente l’articolo a cui ci riferiamo.

Ora abbiamo bisogno di un modo per visitare questo percorso cliccando su un link dalla homepage senza dover ricaricare l’intera pagina. Questo è possibile grazie al magico strumento di Inertia <Link>. Nell’introduzione abbiamo detto che Inertia usa <Link> come involucro per un tag di ancoraggio standard <a>, e che questo involucro ha lo scopo di rendere le visite alla pagina il più fluide possibile. In Inertia, il tag <Link> può comportarsi come un tag di ancoraggio che esegue richieste a <GET>, ma può anche agire come <button> e <form> allo stesso tempo. Vediamo come applicarlo al nostro progetto.

Nel nostro Index.vue, importeremo <Link> da Inertia, rimuoveremo il tag di ancoraggio <a> e lo sostituiremo con il tag <Link> di Inertia. L’attributo href sarà impostato sull’URL del percorso che abbiamo creato in precedenza per visualizzare l’articolo:

<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>

Modifichiamo Show.vue con Tailwind per farlo apparire un po’ più elegante e pronto per la nostra visita. Inoltre, dobbiamo fargli sapere che deve aspettarsi un oggetto “Article” e impostarlo come prop:

<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>

Ora, quando clicchiamo sul titolo dell’articolo o su “Read more”, verremo magicamente trasportati a Show.vue senza dover aggiornare la pagina.

La pagina di esempio
I link di Inertia sono al loro posto.

Nel nostro caso, stiamo usando <Link> come tag di ancoraggio che invia una richiesta GET al percorso e restituisce i nuovi dati, ma possiamo usare <Link> anche per POST, PUT, PATCH e DELETE

“routes/web.php“:

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

Suggerimenti e Trucchi su Laravel Inertia Che Dovresti Conoscere

Ora abbiamo una SPA funzionante costruita con Laravel, Inertia e Tailwind CSS. Ma Inertia può aiutarci a ottenere molto altro. È il momento di acquisire alcune tecniche di Inertia che aiuteranno sia chi sviluppa sia chi visita l’applicazione.

Generare URL

Avrete notato che abbiamo aggiunto nomi ai nostri percorsi Laravel senza usarli. Inertia ci permette di usare le nostre rotte con nome all’interno dei nostri componenti invece di scrivere manualmente il percorso completo.

Possiamo ottenere questo risultato installando il pacchetto Ziggy nel nostro progetto:

composer require tightenco/ziggy

Quindi andate su “resources/js/app.js” e aggiornatelo in questo modo:

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);
	},
});

Andate a “/resources/views/app.blade.php” e aggiornate l’head con la direttiva @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>

…e aggiornate i pacchetti NPM con i due comandi da terminale che seguono:

npm install && npm run dev

Questo pacchetto ci permette di usare i percorsi con nome all’interno dei nostri componenti Inertia, quindi andiamo su Index.vue e rimuoviamo il vecchio percorso manuale sostituendolo con il nome del percorso e passando i dati normalmente, come se fossimo nel nostro controller.

Sostituiamo questo:

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

… con questo:

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

In questo modo otterremo lo stesso comportamento che avevamo prima, ma ora è più facile per chi sviluppa ed è estremamente utile quando il percorso prevede molti parametri.

Indicatori di Progresso

Questa è una delle caratteristiche più belle di Inertia.js; poiché la SPA offre un’esperienza interattiva all’utente, avere un feedback costante sul caricamento di una richiesta sarebbe un’aggiunta fantastica all’applicazione. Questo può essere realizzato con una libreria separata offerta da Inertia.

La libreria “@inertiajs/progress” è un wrapper attorno a NProgress che mostra in modo condizionato gli indicatori di caricamento in base agli eventi di Inertia. Non è necessario sapere come funziona dietro le quinte, quindi vediamo di farla funzionare.

Possiamo installare questa libreria con il seguente comando da terminale:

npm install @inertiajs/progress

Una volta installata, dobbiamo importarla in “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";
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 });

Questo mostrerà una barra di caricamento e uno spinner di caricamento di colore nero, ma possiamo cambiare il colore e altre opzioni utili che si trovano nella documentazione dell’indicatore di avanzamento di Inertia.js.

L'intestazione blu del
L’indicatore di avanzamento di Inertia (in alto a destra).

Gestione dello Scorrimento

In alcuni casi, potreste voler navigare verso una nuova pagina mantenendo la stessa posizione di scorrimento. Forse ne avrete bisogno se permettete agli utenti di lasciare dei commenti; questo invierà un modulo e caricherà il nuovo commento dal database al vostro componente; vorrete che questo avvenga senza che l’utente perda la posizione di scorrimento. Inertia si occupa di questo.

Nel nostro caso, applichiamola al tag <Link> in Index.vue. Per mantenere la posizione di scorrimento durante il reindirizzamento a un’altra pagina con <Link> di Inertia, basta aggiungere l’attributo preserve-scroll al tag <Link>:

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

Suggerimenti SEO

Sin dalla nascita delle SPA, le persone si sono preoccupate dell’ottimizzazione per i motori di ricerca (SEO). È risaputo che se usate l’approccio SPA, i motori di ricerca avranno difficoltà a scansionare la vostra applicazione web perché tutto è renderizzato lato client, con il risultato che il vostro sito web non compare in cima ai risultati di ricerca; tuttavia, come mai piattaforme popolari come Facebook e Github sono diventate SPA e continuano a ottenere buoni risultati in termini di SEO?

Beh, questa non è più una missione impossibile. Inertia offre alcune soluzioni per aiutare la vostra SPA a diventare SEO friendly.

Inertia Vue SSR con Laravel e Vite

I motori di ricerca cercano sempre l’HTML sul vostro sito web per identificarne il contenuto; tuttavia, se non avete l’HTML negli URL, questo lavoro diventa più difficile. Quando sviluppate una SPA, tutto ciò che avete sulla vostra pagina è JavaScript e JSON. Inertia ha introdotto una funzione di Server-Side Rendering (SSR) che potete aggiungere alla vostra applicazione. Questa funzione permette alla vostra applicazione di pre-renderizzare una pagina iniziale visitata sul server e poi di inviare l’HTML renderizzato al browser. In questo modo gli utenti possono vedere e interagire con le vostre pagine prima che vengano caricate completamente, oltre ad avere altri vantaggi, come la riduzione del tempo necessario ai motori di ricerca per indicizzare il vostro sito.

Per riassumere il funzionamento, Inertia identifica se è in esecuzione su un server Node.js e renderizza in HTML i nomi dei componenti, le proprietà, l’URL e la versione delle risorse. In questo modo l’utente e il motore di ricerca avranno a disposizione praticamente tutto ciò che la vostra pagina ha da offrire.

Tuttavia, trattandosi di Laravel, questo non ha molto senso perché Laravel è un framework PHP e non gira su un server Node.js. Pertanto, inoltreremo la richiesta a un servizio Node.js, che eseguirà il rendering della pagina e restituirà l’HTML. In questo modo la nostra applicazione Laravel Vue sarà SEO-friendly di default.

Per prima cosa, dobbiamo installare il pacchetto npm Vue.js SSR:

npm install @vue/server-renderer

Un altro utile pacchetto “NPM” di Inertia fornisce un semplice server “HTTP”. Vi consigliamo vivamente di installarlo:

npm install @inertiajs/server

Poi, in “resources/js/”, aggiungeremo un nuovo file chiamato ssr.js. Questo file sarà molto simile al file app.js che abbiamo creato durante l’installazione di Inertia, solo che verrà eseguito in Node.js e non nel browser:

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),
            	});
    	},
	})
);

Verificate di non includere tutto nel file ssr.js perché non sarà visibile ai visitatori; questo file serve solo ai motori di ricerca e ai browser per mostrare i dati all’interno della vostra pagina, quindi includete solo ciò che è importante per i vostri dati o solo ciò che li renderà disponibili.

“Per impostazione predefinita, il server SSR di Inertia opera sulla porta 13714. Tuttavia, potete cambiare questa impostazione fornendo un secondo argomento al metodo createServer”, si legge su Inertia DOCs.

I DOC di Inertia.js non spiegano come integrare l’SSR di Inertia con Vite, ma lo faremo ora. Andate su vite.config.js e incollate quanto segue:

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,
            	},
        	},
    	}),
	],
});

Successivamente, andate al file package.json e modificate lo script di compilazione:

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

Ora se eseguiamo npm run build, Vite costruirà il nostro bundle SSR per la produzione. Per maggiori informazioni su questo argomento, potete visitare i DOC di Inertia SSR e i DOC di Vite SSR.

Titolo e Meta

Poiché le applicazioni JavaScript vengono rese all’interno del documento <body>, non possono rendere il markup del documento <head> perché non rientra nel loro ambito. Inertia dispone di un componente <Head> che può essere utilizzato per impostare i tag <title>, <meta> e altri componenti <head> della pagina.

Per aggiungere l’elemento <head> alla vostra pagina, dobbiamo importare <Head> da Inertia come abbiamo fatto per il componente <Link>:

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

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

Possiamo anche aggiungere un titolo globale per tutte le pagine, in modo da aggiungere il nome della vostra applicazione accanto al titolo di ogni pagina. Lo abbiamo già fatto nel file app.js:

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

Ciò significa che se aggiungiamo <Head title="Home"> alla homepage della nostra applicazione con un titolo, questo verrà visualizzato in questo modo: <title>Home - My App</title>.

Monitoraggio dell’Applicazione

La velocità è uno dei fattori più importanti per ottimizzare le prestazioni SEO del vostro sito web. Se usate WordPress per il vostro sito web, Kinsta APM vi aiuterà a monitorare e a tenere sotto controllo la vostra applicazione. Questo vi aiuta a identificare i problemi di prestazioni di WordPress ed è disponibile gratuitamente su tutti i siti ospitati da Kinsta.

Riepilogo

Inertia.js è una delle tecnologie più significative disponibili; unitela a Laravel e avrete una moderna applicazione Single-Page costruita con PHP e JavaScript. Taylor Otwell, il creatore di Laravel, è talmente interessato a Inertia che Laravel ha lanciato i suoi starter kit più popolari, Laravel Breeze e Jetstream, con il supporto di Inertia e SSR.

Se siete fan di Laravel o lavorate nello sviluppo, Inertia.js attirerà senza dubbio la vostra attenzione. In questo tutorial abbiamo realizzato un blog molto semplice e basilare in pochi minuti. C’è ancora molto da imparare su Inertia e questo potrebbe essere solo il primo di molti articoli e tutorial.

Quali altri aspetti di Laravel vorreste che esplorassimo? Fatecelo sapere nella sezione commenti qui sotto.

Mostafa Said

Sono Mostafa, uno sviluppatore full-stack appassionato di Laravel, Inertia e dei framework JavaScript. Quando non sto programmando, mi trovate a condividere le mie conoscenze attraverso tutorial, a partecipare ad hackathon (e a vincerne alcuni) e a diffondere l'amore per la tecnologia insegnando quello che ho imparato.