De samenwerking met Laravel en Vue.js vereenvoudigen met Inertia

Auteur: Jelmer Portegijs

Inertia is een nieuw framework van Jonathan Reinink voor het maken van Single Page Applications (SPA), waarbij server-side en client-side samenwerken. Inertia moet niet gezien worden als een vervanging voor je front-end of back-end frameworks, maar meer als een toevoeging op je bestaande project. Inertia is gemaakt voor drie front-end frameworks: React, Vue.js en Svelte en voor twee back-end frameworks: Laravel en Ruby on Rails.

In deze blog leg ik uit hoe Inertia werkt en hoe je deze in een Laravel en Vue.js project kan toepassen.

Hoe werkt het

Wanneer je Inertia voor het eerst opstart zal Inertia een volledige HTML response versturen. Alle overige paginabezoeken zullen via een AJAX call (XHR) ingeladen worden. Dankzij de AJAX call (XHR) hoeven de pagina’s niet opnieuw geladen te worden en krijg je het gevoel van een snelle SPA.

In de volledige HTML pagina, zoals hierboven beschreven, wordt een object (zie data-page attribute in het voorbeeld hieronder) ingeladen. Dit object bevat data, afkomstig van de server, die jouw pagina nodig heeft. Data zoals:

  • JavaScript pagina component dat ingeladen wordt;
  • Pagina data, ook wel “props” genoemd;
  • URL van de pagina;
  • Versie van je JavaScript bundel.

De HTML pagina is het begin van jouw applicatie en ziet er, na integratie van Inertia, ongeveer zo uit:

<html>
<head>
   <title>My app</title>
   <link href="/css/app.css" rel="stylesheet">
</head>
<body>

   <div id="app" data-page="{"component":"Event","props":{"event":{"id":80,"title":"Birthday party","start_date":"2019-06-02","description":"Come out and celebrate Jonathan's 36th birthday party!"}},"url":"/events/80","version":"c32b8e4965f418ad16eaebba1d4e960f"}"></div>

   <script src="/js/app.js" defer></script>
</body>
</html>

Overige pagina bezoeken

Alle overige pagina bezoeken worden gedaan via XHR. Inertia verstuurd deze door middel van een JSON response. Deze JSON wordt encoded geladen in het beschikbare data-page attribuut. De pagina ziet dat de JavaScript versie afwijkt van de vorige en rendered zo nieuwe Vue componenten.

Aan de slag met Inertia

Zoals je las in de introductie heeft Inertia toevoegingen op zowel frontend als backend. De één kan niet werken zonder de ander. Daarom is integratie van beide verplicht. Laten we eens kijken hoe dat eruit ziet.

Backend - Laravel

Als je bekend bent met PHP en Laravel dan weet je dat Composer tegenwoordig niet te missen is.

Om Inertia te installeren in je Laravel project, open je een Terminal. Hiermee navigeer je naar je Laravel project en installeer je Inertia met het volgende commando:

composer require inertiajs/inertia-laravel

Nu Inertia geïnstalleerd is kunnen we het in gebruik gaan nemen. Hiervoor is een root weergave nodig. Bij een nieuwe Laravel installatie is dat het “resources/views/welcome.blade.php” Blade bestand. Inertia zoekt standaard naar een “app.blade.php” bestand, hernoem daarom je “welcome.blade.php” naar “app.blade.php”.

Nu kunnen we deze view opschonen tot er ongeveer het volgende staat:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <link href="{{ mix('/css/app.css') }}" rel="stylesheet" />
  </head>
  <body>
    @inertia

    <script src="{{ mix('/js/app.js') }}" defer></script>
  </body>
</html>

We laden CSS en JavaScript in via Laravel Mix. Hierin geven we aan waar Inertia, de aanvankelijke HTML pagina en alle pagina’s via XHR, mag laden.

Wil je liever een andere root weergave gebruiken, dan kan je dit veranderen met Inertia::setRootView(). Om een voorbeeld uit mijn eigen project te geven, ziet het er als volgt uit:

if (request()->is(['admin', 'admin/*'])) {
    Inertia::setRootView('admin.app');
}

Inertia::setRootView('app');

Componenten aanroepen

Zoals je gewend bent bij Laravel roep je op deze manier een weergave aan vanuit je controller of route:

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('user', [
            'users' => User::all(),
        ]);
    }
}

Met Inertia return je geen weergave, maar een Vue component:

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\User;
use Inertia\Inertia;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Inertia::render('User/Index', [
            'users' => User::all(),
        ]);
    }
}

Deze twee voorbeelden verschillen weinig van elkaar en dat is juist het mooie aan Inertia. Je kunt gewoon variabelen door blijven sturen naar de weergave zoals je dat gewend bent. Het enige verschil is dat de genoemde variabelen nu direct te gebruiken zijn in je Vue component.

Frontend - Vue.js

Zoals Composer een standaard is geworden bij PHP, is NPM in grote lijnen hetzelfde, alleen dan voor JavaScript. Als je nog geen NPM geïnstalleerd hebt dan raad ik je aan dit eerst te doen.

Allereerst moet Vue.js geïnstalleerd worden. Mocht je dit nog niet geïnstalleerd hebben dan kun je het volgende commando in een Terminal venster uitvoeren:

npm install vue

Om Inertia te installeren in je Laravel project, open je een Terminal. Hierin navigeer je naar je Laravel project en plak je de volgende commando in de Terminal:

npm install --save @inertiajs/inertia @inertiajs/inertia-vue

Nu kunnen we de inertia-vue plugin gebruiken in ons JavaScript bestand. Bij een nieuwe Laravel installatie is dat: “resources/js/app.js”. Daar kan je direct onder javascript require('./bootstrap'); het volgende toevoegen:

import { InertiaApp } from '@inertiajs/inertia-vue';
import Vue from 'vue';

Vue.use(InertiaApp);

const app = document.getElementById('app');

new Vue({
  render: h => h(InertiaApp, {
    props: {
      initialPage: JSON.parse(app.dataset.page),
      resolveComponent: name => require(`./Pages/${name}`).default,
    },
  }),
}).$mount(app);

Componenten

Zoals je ziet in de resolveComponent property, zoekt Inertia naar componenten in de Pages map. Zorg er daarom voor dat componenten die je aanroept, vanuit je controller, ook echt in de Pages map staan. In mijn controller voorbeeld roep ik een component “User/Index” aan, dit is een conventie die ik persoonlijk gebruik en hoeft dus niet aangehouden te worden. Deze component is te vinden in “resource/js/Pages/User/Index.vue” en ziet er als volgt uit:

<template>
  <layout>
    <h1>Users</h1>
    <ul>
      <li v-for="user in users" :key="user.id">
        <inertia-link :href="`/admin/user/${user.id}`">
          {{ user.first_name }} {{ user.last_name }}
        </inertia-link>
      </li>
    </ul>
  </layout>
</template>

<script>
import Layout from '@/Shared/Layout';

export default {
  props: ['users'],
}
</script>

Props

Props is een verkleinwoord voor properties. In het geval van Inertia zijn het de variabelen die we vanuit de controller doorsturen. We kunnen props gebruiken door ze te definiëren in het Vue component en ze exact te noemen zoals in de controller. Nu kunnen we deze variabel gebruiken in het Vue component.

Tot slot

De samenwerking met Laravel en Vue is voor mij nog nooit zo gemakkelijk geweest met dank aan Inertia. Ben je geïnteresseerd geraakt in Inertia? Volg dan ook deze Twitter accounts: