この記事ではLaravelにInertia.jsをインストールしてHello Worldを出力する方法までを解説します。
前の記事でInertiaについては大体わかりましたので、どうやって使うか教えてください!
では今回はLaravelにInertia.jsを導入して簡単なサンプルを作成してみましょう!
前回の記事ではInertia.jsについて、どういうものか概要を解説しました。そちらも合わせてお読みください。
Laravelのインストール
この記事では、Laravel10を用いて解説を進めていきます。Laravelの環境構築についてはこちらの記事も参考にするとDockerを使用して楽にLaravelの開発環境を構築できます。このシリーズ記事では下記のリポジトリからDockerでのLaravel環境をクローンして行っていきます。
Dockerのインストールは事前に済んでいるものとします。
DockerでLaravel開発環境をクローン
cd /home/ship/project/
git clone https://github.com/shipwebdotjp/docker-laravel.git inertiademo-laravel10
cd inertiademo-laravel10/
まず、お好きな場所で構いませんが、プロジェクトを作成したいディレクトリに移動します。その後、リポジトリからDockerのLaravel環境のテンプレートをダウンロードして、そのプロジェクトディレクトリに移動します。
make create-project
Dockerコンテナの作成とLaravelのインストールを行います。
以降のコマンドはLaravelをインストールしたディレクトリで実行します。上記のDockerでLaravel環境を構築した場合は、「make app」または「docker-compose exec app bash」を実行してLaravelのコンテナに入ってから実行して下さい。
make app
docker-compose exec app bash
root@861c134e5ba8:/work/backend# (Laravelコンテナの中)
サーバーサイドの設定
Inertia.jsはサーバーサイドとクライアントサイド両方に関わってきます。まずはサーバーサイドの設定をしていきます。スターターキットと呼ばれる、認証システムなどがセットになったパッケージをインストールすると自動的にInertiaの環境が揃います。ただ、今回は最小限の構成にしたいのと、理解を深めるためにスターターキットを使用せずInertiaを使える状態にしていきます。
手動でInertia.jsをインストール
composer require inertiajs/inertia-laravel
composerを利用してInertiaの依存パッケージをインストールします。
Using version ^0.6.9 for inertiajs/inertia-laravel
./composer.json has been updated
Running composer update inertiajs/inertia-laravel
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking inertiajs/inertia-laravel (v0.6.9)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Downloading inertiajs/inertia-laravel (v0.6.9)
- Installing inertiajs/inertia-laravel (v0.6.9): Extracting archive
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
INFO Discovering packages.
inertiajs/inertia-laravel ....................................................................................... DONE
laravel/sail .................................................................................................... DONE
laravel/sanctum ................................................................................................. DONE
laravel/tinker .................................................................................................. DONE
nesbot/carbon ................................................................................................... DONE
nunomaduro/collision ............................................................................................ DONE
nunomaduro/termwind ............................................................................................. DONE
spatie/laravel-ignition ......................................................................................... DONE
83 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force
INFO No publishable resources for tag [laravel-assets].
ルートテンプレートの編集
resources/views/ディレクトリにapp.blade.phpというファイルを以下の内容で作成します。これがInertiaでアクセスした際のデフォルトのテンプレートになります。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
@vite('resources/js/app.js')
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
ミドルウェアの作成
Inertiaリクエストを処理するためのミドルウェアの作成を次のartisanコマンドで行います。
php artisan inertia:middleware
INFO Middleware [app/Http/Middleware/HandleInertiaRequests.php] created successfully.
ミドルウェアの登録
app/Http/Kernel.phpを開き、$middlewareGroupsのwebへ、いま作成したHandleInertiaRequestsミドルウェアを登録します。
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\HandleInertiaRequests::class,//←追加
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
スターターキットを使用する方法
jetstreamなどを利用するとInertia+Vue+TailwindCSSの環境が一発で整いますが、今回はjetstreamの認証は必要ないため、jetstreamなどのスターターキットを使う方法は詳しく解説は行いません。
ルーティングとレスポンスを作成
では実際にInertiaを使った表示を行うため、ルーティングを管理している「routes/web.php」を編集し、コントローラーを作成せずに直接ビューを返すルーティングを追加してみましょう。
「/HelloWorld」にアクセスがあった時、「HelloWorld」ビューを返すInertiaレスポンスは次のように書きます。これを「routes/web.php」の末尾に追記します。
use Inertia\Inertia;
Route::get('/HelloWorld', function () {
return Inertia::render('HelloWorld');
});
サーバー側の設定は以上です。次に、フロント側の設定に移ります。
フロントエンド側の設定
nodeを使用してインストールを行うため、一旦appコンテナから出て、webコンテナに入ります。
exit
make web
その後、フロントエンドで使う技術に合わせたパッケージをインストールします。
手動での依存パッケージのインストール
今度はクライアントサイドで使用する、Vue.jsのコンポーネントを作成していきます。
Inertia.jsはVue2,Vue3,React,Svelteで使用できますが、このシリーズ記事ではVue3を用います。
まずVue3をViteで扱えるようにします。ViteのVueプラグインをインストールします。
npm install @vitejs/plugin-vue --save-dev
vite.config.jsを修正して、Vueファイルを処理できるようにします。
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from "@vitejs/plugin-vue"; //←追加
export default defineConfig({
plugins: [
vue(), //←追加
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});
Inertia.jsをVue3で使用するためのライブラリをインストールします。
npm install @inertiajs/vue3
Inertiaアプリの初期化
メインのJavaScriptファイルとなるresources/js/app.jsでInertiaアプリの初期化を行います。
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
return pages[`./Pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})
Vue.jsのPageコンポーネントを作成
resources/js/配下にPagesディレクトリを作成し、そこにVue.JSのコンポーネントを作成します。
「HelloWorld.vue」というファイル名でHello Worldと表示するだけのコンポーネントファイルを作成します。
<template>
<div>
Hello world!
</div>
</template>
保存したらWebコンテナで下記を実行します。これでpackage.jsonのscriptのdevで定義されているコマンドが実行されます。
npm run dev
Laravel10ではフロントエンドのバンドルツールとして旧来のwebpackではなく、Viteが採用されているため、Viteが起動します。
/work/backend # npm run dev
> dev
> vite
VITE v4.4.9 ready in 409 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
LARAVEL v10.18.0 plugin v0.7.8
➜ APP_URL: http://localhost
アセットファイルの更新を検知して自動的にコンパイル+ホットリロードしてくれます。
Dockerで実行している場合、Viteのサーバーにホストからアクセスできるようvite.config.jsに設定を加える必要があります。
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue(),
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
//この下の部分を追加
server: {
host: true,
hmr: {
host: 'localhost',
},
},
});
「http://localhost/HelloWorld」にアクセスして「HelloWorld」と表示されていれば成功です。
TailwindCSSのインストール
見た目を整えるため、このシリーズではTailwindCSSを使用します。見た目はどうでもいいという方はこのステップは飛ばしていただいても構いません。
npm install -D tailwindcss postcss autoprefixer
added 83 packages, and audited 136 packages in 11s
28 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
上記のnpm installコマンドでtailwindcss、postcss、autoprefixerをインストールします。
npx tailwindcss init
/work/backend # npx tailwindcss init
Created Tailwind CSS config file: tailwind.config.js
tailwindcssの初期化コマンドでtailwind.config.jsが作成されるので、content配列部分に、どの種類のファイルのTailwindCSSクラスを処理するかを指定します。ここではblade、js,vueファイルを指定します。
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
"./resources/**/*.vue",
],
theme: {
extend: {},
},
plugins: [],
}
次にpostcssの設定ファイルpostcss.config.cjsを新規作成します。(拡張子が.jsではなく、.cjsなことに注意してください)
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
CSSのJavaScript埋め込みを用いるので、vite.config.jsのlaravelのエントリーポイントからcssは削除します。
laravel({
input: ['resources/js/app.js'], //←'resources/css/app.css'を削除
refresh: true,
}),
代わりにJavaScriptのエントリーポイントでCSSもimportするようにします。
import '../css/app.css';
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
return pages[`./Pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})
CSSエントリーポイントのresources/css/app.cssにデフォルトのtailwindCSSの各クラス設定を読み込むよう設定します。
@tailwind base;
@tailwind components;
@tailwind utilities;
Linkの動作確認
Inertia.jsでは、<Link>というコンポーネントを使用してリンクを作成すると、クリックした際にXHRでのリクエストがされるようになります。この動作を確認してみます。
「HelloWorld.vue」にホームへ戻る<Link>を追加します。
<script setup>
import { Link } from '@inertiajs/vue3'
</script>
<template>
<div>
Hello world!
<Link href="/" class="text-sm text-blue-700 underline">
ホームへ戻る
</Link>
</div>
</template>
「http://localhost/HelloWorld」にアクセスすると「HelloWorld ホームへ戻る」と表示されます。
ブラウザの開発者ツールでネットワークを見てみると、HTMLが返ってきていることが分かります。このように初回のアクセスはフルHTMLが返ります。
インスペクタでDOMツリーをみると、リンクは普通にaタグで作成されていることが分かります。ただし、クリックイベントを補足するイベントリスナーが登録されており、クリック時に通常のリクエストを送るのではなく、XHRリクエストを送信するメソッドが呼び出されているということが推察できます。
<a class="text-sm text-blue-700 underline" href="http://localhost/"> ホームへ戻る </a>
次に「ホームへ戻る」のリンクをクリックするときのリクエストを見てみます。
X-Inertia: true
X-Requested-With: XMLHttpRequest
「X-Inertia」ヘッダーが付加されてXMLHttpRequest(XHR)で送信されていることが分かります。
レスポンスは、今アクセスしたのはInertia化していないトップページのためtext/htmlが返ってきて、モーダルで表示されます。
リンク先をInertiaで動かしているページに変更すると、動作が変化します。
試しに自分自身のページを呼び出すようにHelloWorld.vueを変更してからリンクをクリックしてみます。
<Link href="/HelloWorld" class="text-sm text-blue-700 underline">
HelloWorld
</Link>
Content-Type: application/json
この場合、JSONオブジェクトが返ってきます。それでも画面上はHello Worldが表示されています。このようにInertia.jsが、クライアントサイドではリンクをXHRに変えてリクエストを送り、サーバーサイドではX-Inertiaヘッダーの有無をチェックしてフルHTMLを返すかJSONデータのみを返すかを決定していることが分かります。
ソースコード
今回のソースコードはGitHubで公開しています。
Comments