再びSPA(シングルページアプリケーション)への挑戦です! 右のTAGSで「SPA」をクリックしてもらえばわかりますが過去にいくつか私のキャパ内でSPAに挑戦しております。しかし適用した技術がLaravelとの関連がまったくなかったり、完全にjQueryを脱却できなかったりと、どうも私の中ではしっくりこない。でも今度は違います。Inertia(イナーシャ)を使うからです。

Inertia

SPAと言えば、フロントエンドのReacdtJSやVueJSのようなjavascriptのフレームとバックエンドのapiで開発するものと思っていたのですが、Inertiaを使うとなんとLaravelのRouteやコントローラをそのまま使いSPAが可能となります。しかも、コントローラに使用するテンプレートはBladeの代わりにVueJSのコンポーネントとなります。HTMLの要素を含めてです。それゆえに、今まではBladeにおいてjQueryを使いボタンのクリックイベントとかでフロントエンドのプログラムをしていたところ、jQueryをまったく使わずにVueJSだけでプログラムできるのです。

ちなみに、Inertia、日本語直訳では「怠惰」。レイジーな私にぴったりで、これほどわくわくするものはありません。

スターターキット

このInertiaを簡単に体験してもらうために、hikaru氏の協力を得て以下の組み合わせのスターターキットを作成してみました。

Laravel Breeze

以前はLaravel UIというユーザー認証に必要な機能を含むスターターパッケージがありましたが、現在はその変わりにLaravel Breezeとなっています。ユーザーの登録、ログイン、ログアウト、パスワードリセットの対応の機能が含まれています。

Inertia

SPA対応のパッケージで、composerを通してのバックエンドのためのphpコード、npmを通してのフロントエンドのためのjavascriptが提供されています。

Vue 3

Inertiaのフロントエンドは先にも述べたようにReactJSやVueJSなどを採用しています。VueJSにおいてはバージョン2と去年にリリースされたバージョン3に対応しています。ここでは、Composition APIを使用してより理解しやすくなったバージョン3をコンポーネントに採用しています。

Bootstrap 5

超有名なCSSのフレームワークですが、バージョン5では従来のバージョンと異なり、jQueryに頼らず全て純のJavascriptで書かれています。ちなみに、Laravel Breezeでのインストールの選択においてInertiaを選択することが可能ですが、そこでインストールされるCSSフレームワークはTailwind CSSです。しかし、それはまた違うパラダイムゆえにここではお馴染みのBootstrapに書き換えています。

日本語版

最後に、日本語の開発者のために、Vueのコンポーネント、バリデーションなどにおいて日本語訳を提供しています。

インストール

以下、スターターキットのインストールの手順です。

まず以下のレポからクローンします。

$ git clone git@github.com:lotsofbytes/breeze-bootstrap.git

breeze-bootstrapというサブディレクトリーが作成されるます。この時点のmainブランチは英語版なので、dev-japanese-verにブランチに以下で変更します。

$ cd breeze-bootstrap
$ git checkout dev-japanesse-ver

以下はお馴染みのLaravelの設定です。

$ cp .env.example .env # .envを必要に応じて編集してください。
$ composer install
$ php artisan key:gen
$ npm install
$ php artisan migrate --seed

そしてウェブサーバーを立ち上げます。

$ php artisan serve

最初の画面です。

ユーザーを登録してみましょう。

エラーも表示されますね。
登録が成功すると、ログイン後のダッシュボード画面になります。

Routeはどう変わった?

ブレードを使用するrouteの設定とInertiaを比較してみましょう。

これがお馴染みのブレード版です。

...
Route::get('/', function () {
    return view('welcome');
});

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Inertia版は、

...
Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
...

基本的にview()Inertia::render()に代わっただけです。関数の引数は、ブレードからVueのコンポーネントとなっていて、ブレード同様に変数を渡すことが可能です。ちなみに、inertia()というヘルパーがあるので以下のようにも書けるようです。

Route::get('/dashboard', function () {
    return inertia('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

コントローラでの変更は?

今度はコントローラの部分、こちらはどう変わったのか、見てみましょう。

...
class RegisteredUserController extends 
{
    /**
     * Display the registration view.
     *
     * @return \Inertia\Response
     */
    public function create()
    {
        return Inertia::render('Auth/Register');
    }

    /**
     * Handle an incoming registration request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect(RouteServiceProvider::HOME);
    }
}

上はユーザー登録画面のコードですが、見ての通り、変わったのは入力画面表示においてInertia::render()となった部分だけです。情報保存のためのstore()メソッドは変更なしです。

フロントエンドのファイルはどこへ?

フロントエンドに関わるファイルは、ブレード版ではみなresources/viewsのディレクトリ下に収められていましたが、InertiaのVueコンポーネントのファイルはみなresouces/js下となります。こんな感じです。

.
├── app.js
├── bootstrap.js
├── Components
│   ├── ApplicationLogo.vue
│   └── ValidationErrors.vue
├── Layouts
│   ├── Authenticated.vue
│   └── Guest.vue
└── Pages
    ├── Auth
    │   ├── ConfirmPassword.vue
    │   ├── ForgotPassword.vue
    │   ├── Login.vue
    │   ├── Register.vue
    │   ├── ResetPassword.vue
    │   └── VerifyEmail.vue
    ├── Dashboard.vue
    └── Welcome.vue

ブレードがすべてなくなったわけではありません。大元として1つだけブレードファイルがあります。

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

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

        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">

        <!-- Styles -->
        <link rel="stylesheet" href="{{ url(mix('css/bootstrap.css')) }}">
        <link rel="stylesheet" href="{{ url(mix('css/app.css')) }}">

        <!-- Scripts -->
        @routes
        <script src="{{ url(mix('js/app.js')) }}" defer></script>
    </head>
    <body class="font-sans antialiased">
        @inertia
    </body>
</html>

@inertiaの部分に先のVueのコンポーネントから作成されたHTMLが入り込み、アクセスしたページにより内容が変わります。

Vueのファイルを変更したら

このスターターキットをインストールしたら、まずはVueのコンポーネントをいじってみたいはず。ブレードでは変更して保存したら、画面を更新すれば変更がすぐに反映されます。同様なことをしたいなら、以下のように、

$ npm run watch

を実行しておきます。この実行は通常のコマンドのようにすぐに終了するのではなく常に走り続けます。それゆえに、関わるファイルが変更されると自動的に、Laravel-mixを通してcssやjsファイルをコンパイルしてくれます。

そこで表示されたファイルは、publicのディレクトリ下のファイルとして保存されます。コンパイルが完了したら、ブレードと同様に画面を更新してcssやjsファイルを読み直すと変更が反映されます。

最終的には、以下の実行で最適化、最小化されたProduction Readyのファイルの作成が可能です。

$ npm run prod

最後に

Inertiaを使っての開発がより楽しくなるように、いろいろ将来の投稿を考えています。お楽しみに。

By khino