Table of Contents
はじめに
Inertia.jsはモノリシックなSPAアプリを開発する場合には非常に強力なライブラリで、バックエンド側はLaravel/Rails、フロントエンド側はVue2/Vue3/React/Svelteで利用できます。
今回はLaravelのスターターキットBreezeを使って、Laravel + Inertia.js + Vue3環境を構築し、TypeScriptを導入する手順を紹介します。
環境
- Vite v4.0.0
- Vue v3.2.41
- Laravel v9.19
Laravelプロジェクトを作成する
最初にLaravelプロジェクトを新規で作成します。
スターターキットのBreeze を利用するとInertiaとVue3がセットアップ済みのプロジェクトが作成されます。
$ curl -s "https://laravel.build/laravel-inertia-vue-app?with=pgsql" | bash
$ cd laravel-inertia-vue-app
# Dockerコンテナ起動
$ ./vendor/bin/sail up -d
# Breezeのインストール
$ ./vendor/bin/sail composer require laravel/breeze --dev
$ ./vendor/bin/sail artisan breeze:install vue
# マイグレーションコマンド実行
$ ./vendor/bin/sail artisan migrate
# Viteの開発サーバー起動
$ ./vendor/bin/sail npm install
$ ./vendor/bin/sail npm run dev
無事にViteの開発サーバーが起動したら http://localhost へアクセスすると画面が見られるようになります。
Seedファイルを用意する
Breezeですでに用意されたダッシュボード画面へログインしたいので、初期ユーザーを作成するSeedファイルを用意します。
database/seeders/DatabaseSeeder.phpを以下のように編集します。
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
\App\Models\User::factory(10)->create();
\App\Models\User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
}
Seedコマンドを実行してDBにユーザーデータを追加します。
$ ./vendor/bin/sail artisan db:seed
http://localhost/login へアクセスして test@example.com/password
でログインするとダッシュボード画面へ遷移されます。
.vueファイルをTypeScriptで書く
Breezeで作成したプロジェクトはVite環境のため、とくに設定は何もせずにそのままTypeScriptが動きます。
resources/js/Pages/Dashboard.vueを以下のように書き換えることができます。
<script setup lang="ts">
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { Head, usePage } from '@inertiajs/inertia-vue3';
type Page = { auth: { user: { name: string, email: string }} }
const page = usePage<Page>();
</script>
<template>
<Head title="Dashboard" />
<AuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Dashboard</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="font-medium text-base text-gray-800">{{ page.props.value.auth.user.name }}</div>
<div class="font-medium text-sm text-gray-500">{{ page.props.value.auth.user.email }}</div>
</div>
</div>
</AuthenticatedLayout>
</template>
usePageはInertia側で提供している関数で、バックエンド側で共通データとして渡ってきています。
渡す共通データを定義している箇所はapp/Http/Middleware/HandleInertiaRequests.phpで行われているので確認してみてください。
型チェックをする
TypeScriptを導入したので、CIなどで型チェックすると思います。
そこでvue-tscをインストールして型チェックコマンドを追加します。
$ ./vendor/bin/sail npm install -D vue-tsc
# tsconfig.jsonを作成
$ npx tsc --init
今回は作成したtsconfig.jsonを以下のように設定しました。
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@/*": ["resources/js/*"]
},
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"allowJs": true,
"types": [
"vite/client"
]
},
"include": ["resources/js/**/*"],
"exclude": ["node_modules", "public"],
}
その後、package.jsonへ以下のようなコマンドを追加します。
{
"scripts": {
"type:check": "vue-tsc --noEmit"
},
}
追加後は以下コマンドで型チェックが走るようになったので、CI上などで実行してチェックできるようになります。
$ ./vendor/bin/sail npm run type:check
エントリファイルを用意する
resources/js/app.jsをapp.tsに変えて以下のように記述します。
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue') as any),
setup({ el, app, props, plugin }) {
createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });
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>
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap">
<!-- Scripts -->
@routes
<!-- app.js→app.tsに変更 -->
@vite(['resources/js/app.ts', "resources/js/Pages/{$page['component']}.vue"])
@inertiaHead
</head>
<body class="font-sans antialiased">
@inertia
</body>
</html>
vite.config.jsもvite.config.tsへファイル名を変更します。そしてinputの記述も変えます。
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.ts', // app.js→app.tsへ変更する
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
Ziggy関連のTSエラー
つまずいた点としてZiggy周りのTSエラーでした。
.vueファイルではZiggyが提供しているグローバル関数である route()
をscriptタグやtemplateタグ内でよく使います。
これはLaravel側からレンダリング時にグローバル関数として用意されるものであるため、TSで認識されておらず、TSエラーになります。
これを解消するために別途.d.ts
ファイルを用意する必要がありました。
$ ./vendor/bin/sail npm install -D @types/ziggy-js ziggy-js
resources/js/index.d.tsを作成して以下を記述します。
import { Config, RouteParam, RouteParams, Router } from "ziggy-js";
declare global {
declare function route(
name?: undefined,
params?: RouteParamsWithQueryOverload | RouteParam,
absolute?: boolean,
config?: Config
): Router;
declare function route(
name: string,
params?: RouteParamsWithQueryOverload | RouteParam,
absolute?: boolean,
config?: Config
): string;
declare const Ziggy: any;
}
declare module "vue" {
interface ComponentCustomProperties {
route: ((
name?: undefined,
params?: RouteParamsWithQueryOverload | RouteParam,
absolute?: boolean,
config?: Config
) => Router) &
((
name: string,
params?: RouteParamsWithQueryOverload | RouteParam,
absolute?: boolean,
config?: Config
) => string);
}
}
もっと良さげな書き方があるかもしれませんが、とりあえず @types/ziggy-js
で定義されているroute関数の型を引っ張ってきてscriptタグとtemplateタグ内で使えるようにしました。
LaravelのModelをTypeScriptの型として使えるようにする
このようなモノリシックなLaravelとTypeScriptプロジェクトで開発を続けていると発生する問題の1つに「LaravelのModelをTypeScriptで同じような型定義をしなければならない」というむだな作業が発生すると思います。
それを解決してくれるライブラリでlaravel-typegen というものがあります。
これはLaravel専用のnpmライブラリで、LaravelのModel/Enum/Routing情報をTypeScriptの型定義ファイルとして生成してくれるものです。
詳しい利用方法はここでは紹介しませんが、興味のある方はREADMEをご覧ください。
おわりに
今回Breezeで構築したプロジェクトからTypeScriptの導入までやってみました。
Vite環境だったおかげですぐにTypeScriptが動いて、ほぼjsをtsに書き換える作業だけで終わりました。
これでより安全なコードが書けるようになると思いますので、Laravel + Inertia.jsプロジェクトでTypeScriptを導入してみてはいかがでしょうか。
Related Posts
Daiki Urata
2023/05/22
Daiki Urata
2022/08/29