はじめに
以前Laravel Breezeをインストールした際にパッケージに含まれていたInertiaを使ってみたので簡単に紹介したいと思います。
今回はLaravel + Reactの組み合わせで使用しています。
Inertiaとは
バックエンドフレームワーク(Laravelなど)とフロントフレームワーク(VueやReact)の橋渡しの様な事を行うライブラリの様なもので、Laravelで言うBladeと同じ感覚でVueやReactを使用してのSPAが簡単に作成出来る様にするものです。
公式に「 it’s fine-tuned for Laravel」とあるので、特にLaravel利用を想定して作成されているようです。
ルーティング
Laravelのルーティングを通常通りそのまま使用出来ます。
web.php
1 2 3 4 | // prefix指定して、name付け Route::prefix('sample')->name('invoice')->group(function () { Route::get('/', [SampleController::class, 'index'])->name('Index'); }); |
LaravelからReactに値渡し
Bladeを使うのと同じ様に、使用するReactのコンポーネントを指定して利用する値を引数に指定するだけでLaravelからReatcに値を渡す事が出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 | public function index(Request $request) { // Reactに渡す値 $foo = 'foo'; $bar = 'bar'; // 第一引数にコンポーネント、第二引数に渡したい値を指定 return Inertia::render('Sample/Index', [ 'foo' => $foo, 'bar' => $bar ]); } |
フロント側で
console.log
すると、Propsに渡っているのが確認出来ます。
例えばモデルのコレクションを渡して、一覧表示みたいな事が簡単に出来ます。
Laravel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public function index(Request $request) { // Reactに渡す値 $foo = 'foo'; $bar = 'bar'; // モデル取得してReactに渡す $users = User::all(); // 第一引数にコンポーネント、第二引数に渡したい値を指定 return Inertia::render('Sample/Index', [ 'foo' => $foo, 'bar' => $bar, 'users' => $users ]); } |
React
props.users
でpropsからユーザー一覧情報を取得しています
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | export default function Index(props) { console.log('props:', props) return ( <AuthenticatedLayout auth={props.auth} header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Profile</h2>} > <Head title="Sample" /> <div className="py-12"> ユーザー一覧 <br/> {props.users.map((user, index) => { return ( <div key={index}>{user.name}</div> ) })} </div> </AuthenticatedLayout> ); } |
先程のPropsに含まれている
auth
など他の値は、
HandleInertiaRequests
ミドルウェアで設定されている値です。
ここで設定した値は、常にPropsに含まれる様になるのでコンポーネント関係なくグローバルに値保持したい場合に利用できそうです。
デフォルトではログインユーザー情報を含むauth、バリデーションエラーなどを含むerrors、Laravelのnameルーティング情報を含むziggyの3つが設定されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class HandleInertiaRequests extends Middleware { /** * The root template that is loaded on the first page visit. * * @var string */ protected $rootView = 'app'; /** * Determine the current asset version. */ public function version(Request $request): string|null { return parent::version($request); } /** * Define the props that are shared by default. * * @return array<string, mixed> */ public function share(Request $request): array { // parent::share($request)の中で error が設定されてる。 return array_merge(parent::share($request), [ 'auth' => [ 'user' => $request->user(), ], 'ziggy' => function () use ($request) { return array_merge((new Ziggy)->toArray(), [ 'location' => $request->url(), ]); }, ]); } } |
レスポンス
初回
レスポンスを見てみると、初回のアクセス時はHTMLとアセット(JSやCSS)が返却されており、このHTMLにはReactマウント用のdivタグが含まれています。
このdivタグのdata-pageに
page object
と呼ばれるデータ群が含まれていて、レンダリングの際にバックエンドからフロントエンドに渡る様です。
page object
page objectは以下で構成されているデータ群。
component: 表示しているコンポーネント名
props: バックエンドから渡される値
url: 表示画面のURL
version: アセットのバージョン管理用の値
2回目以降
2回目以降のアクセスでは、リクエストヘッダーに
X-Inertia: true
が含まれます。
このリクエストを検知するとInertiaの方でpage objectのみを含んだJSONレスポンスが返却されます。
リロード時
通常、Propsは常に全て渡されますが、定数系の値など、中には初回以降は必要ない値もあると思います。
クロージャーで書く事で、リロード時にこうした値をPropsに含めない用に設定する事が可能です。
DBアクセスを含む不要な値を含めない様にする事で表示速度パフォーマンスを高める事が期待できます。
例えば以下の場合だと、
レスポンスのpropsに、初回は
users
と
foo
が含まれ、リロード時は
bar
のみが含まれます。
Laravel
Propsに含める変数の取得方法をそれぞれ変更しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public function index(Request $request) { // 初回アクセス時に必ず含まれる // リロート時に含めるか選択出来る // 常に評価される $users = User::all(); // 初回アクセス時に必ず含まれる // リロート時に含めるか選択出来る // 含める時のみ評価される $foo = function () { return 'foo'; }; // 初回アクセス時に含まれない // リロート時に含めるか選択出来る // 含める時のみ評価される $bar = Inertia::lazy(function () { return 'bar'; }); // 第一引数にコンポーネント、第二引数に渡したい値を指定 return Inertia::render('Sample/Index', [ 'foo' => $foo, 'bar' => $bar, 'users' => $users ]); } |
React
ボタンクリック時にリロードしています。
その際に、onlyでレスポンスに含めるPropsを指定しています。
router.reload({ only: ['bar'] })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; import {Head, Link} from '@inertiajs/react'; import { router } from '@inertiajs/react' export default function Index(props) { // リロード // barのみPropsに含める const reload = () => { router.reload({ only: ['bar'] }) } return ( <AuthenticatedLayout auth={props.auth} header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Profile</h2>} > <Head title="Sample" /> <div className="py-12"> ユーザー一覧 <br/> {props.users.map((user, index) => { return ( <div key={index}>{user.name}</div> ) })} </div> <div> <button type="button" onClick={reload} className="bg-blue-500 text-white" >リロード</button> </div> </AuthenticatedLayout> ); } |
結果、barのみ含まれています。
さいごに
ほとんどMPAを作る要領でSPAが作成出来ました。次回はもう少し実践的な内容について書いてみたいと思います。