Inertia.jsは、クライアントサイドとサーバーサイドの両方のレンダリングを組み合わせたSPA(シングルページアプリケーション)を作成することができる先進的な仕組みです。

この記事では、InertiaがいかにSPAの構築を容易にし、開発者が抱える他の多くの問題を解決できるのかをご紹介します。また、このツールの主要な機能も解説します。

それでは、サーバサイドアプリケーションとクライアントサイドアプリケーションがどのように機能するか、という基礎から理解を深めていきましょう。

サーバーサイドレンダリングとは

サーバーサイドレンダリング(SSR)とは、アプリケーションがウェブページのコンテンツをブラウザではなくサーバー側でレンダリングまたは表示できるようにする仕組みを指します。例えばある人が「example.com」にアクセスしようとすると、その人の使用するブラウザはサーバーにリクエストを送信し、この特定のウェブページを表示するのに必要なすべての情報を要求します。サーバーは即座にこれに応答し、完全にレンダリングされたページをブラウザに返します。

検索エンジンは、サーバーから送られた情報がブラウザに到達する前に介入し、インデックスを作成します。ちなみに、これに向けて最適化を行うことをSEO(検索エンジン最適化)と言います。その後、ブラウザがJavaScriptの内容を解釈し、最終的なウェブページがユーザーに表示されます。

サーバー側でレンダリングされたコンテンツを表示する流れ
サーバー側でレンダリングされたコンテンツを表示する流れ

SSRの問題点は、サーバーから完全にレンダリングされたページを読み込むのに時間がかかり、快適なユーザーエクスペリエンスの提供が難しいことです。このような理由で、SPAやクライアントサイドレンダリングが代わりに利用されます。

クライアントサイドレンダリングとは

クライアントサイド レンダリングでは、完全にレンダリング済みのページをサーバーから受け取るのではなく、ウェブページのレンダリングに必要なすべてを(レンダリングされていない状態で)ブラウザが受け取ります。ページが読み込まれると、ブラウザからサーバーへの他のリクエスト送信は無く、ブラウジングが非常に速くなります。

クライアント側でレンダリングされたコンテンツを表示する流れ
クライアント側でレンダリングされたコンテンツを表示する流れ

クライアントサイドレンダリングは、SPAの誕生を助け、ウェブにある種の革命をもたらした。どれだけリンクをクリックしても、ページ全体を再読み込みする必要のないウェブサイトを作ることができます。つまり、ウェブサイト内の移動がスムーズになるということです。

SPAというアイデアは素晴らしいものですが、このアプローチには複雑さやそれなりの問題が付随します。そこで登場するのがInertiaです。サーバーサイドフレームワークを効果的に利用することで、その問題の大部分に対処しています。サーバーサイドアプリケーションとクライアントサイドアプリケーション長所を兼ね備えた手法だと言えます。

Inertia.jsとは

Inertiaは(勘違いされがちですが)JavaScriptのフレームワークというわけではありません。「SPAを開発するための仕組みやツール」と表現する方が正確です。好みのサーバーサイドフレームワークを利用して、それに組み合わせることで、複雑さを伴うことなく優れたSPAを構築することができます。

Inertiaは、既存のフレームワークに取って代わるものではなく、付随するものとして設計されています。より早く、より効率的にタスクを完了するツールです。現在、クライアントサイドレンダリングで3つのフロントエンドフレームワーク(Vue、React、Svelte)、サーバーサイドレンダリングで2つのバックエンドフレームワーク(Laravel、Rails)をサポートしています。

Inertiaはフロントエンドとバックエンドの両方のフレームワークを接続する役割を果たすため、多くのLaravel開発者にとって、SPA構築の信頼できる技術となっています。

Inertia.jsの仕組み

InertiaはVue Routerと似ており、ページ全体を再読み込みすることなく、ページ間の移動を可能にします。また、Inertiaはサーバーサイドフレームワークと同期して動作します。これの実現には、Linkが使用されます。Linkがクリックされると、Inertiaがクリックイベントに介入し、XHRリクエストをサーバーに送信。サーバーはこれがInertiaのリクエストであることを認識して、JSONのレスポンスを返します。このレスポンスにはJavaScriptのコンポーネント名とデータが含まれており、その後、Inertiaは不要なコンポーネントを削除し、新しいページの訪問(表示)に必要なコンポーネントに置き換え、履歴の状態を更新します。

Inertia.jsの機能の深堀り

訪問者が最初に「example.com」を訪れる時、ブラウザからは標準的なフルページリクエストが実行されます。これに対し、サーバーはInertiaが存在しないかのように完全な HTMLのレスポンスを返します。このHTMLレスポンスには、すべてのサイトアセット(CSS、JavaScript)が含まれ、Inertia用のアセットもこれに組み込まれます。より具体的には、初期ページのJSONデータを格納したdata-page属性を持つルートdivとなります。InertiaはこのJSONデータを使用してフロントエンドフレームワークを起動し、初期ページを表示します。

Inertia.js: 初期ページ訪問時のレスポンス
Inertia.js: 初期ページ訪問時のレスポンス

アプリ起動後には、ユーザーがLinkを使って同じドメイン内で訪問する各ルートは、X-Inertiaヘッダを伴うXHRリクエストになります。これは、サーバーに対して、Inertiaのリクエストであることを伝えるためのものです。

それに対するレスポンスは、最初の訪問時のような完全なHTMLファイルではなく、ページコンテンツを格納したJSONとして返されます。

Inertia.js: 後続のルート訪問時のレスポンス
Inertia.js: 後続のルート訪問時のレスポンス

Inertia.jsで解決できること

Inertiaは、ウェブ開発者が抱える多くの問題を解決する優れものです。SPAの設計に伴う複雑な問題のすべてに、素早く効果的に対処するために、Inertiaが開発されました。

SPAの複雑さ

InertiaなしでSPAを構築するとなると、RESTやGraphQL APIを作成し、何らかの認証方法で保護する必要があります。フロントエンドの状態管理システムの作成など、多くの要件が発生します。

そこでInertiaを使うことで、SPAの構築に伴うあらゆる複雑な作業を省くことができます。端的に言えば、バックエンドサーバー上で完全に動作するサーバーサイドアプリケーションをシングルページのJavaScriptアプリケーションに変換可能です。

Inertiaを使用したSPAの作成は、サーバサイドレンダリングアプリの作成と似ています。コントローラを作成し、必要なデータをデータベースに照会し、その結果をビューに返します。

ビューがJavaScriptコンポーネントであるという点が重要です。サーバーからデータを取得し、Inertiaがフロントエンドフレームワークと連携して、データを含むページをJavaScriptファイルとして表示するので、APIを別途構築する必要がありません。

認証

Inertiaはバックエンドからデータとレスポンスをアプリケーションに統合するため、サーバーサイドで使用している認証システムがそのまま使用されます。つまり、クライアントサイドの認証について心配する必要はなく、代わりにサーバーサイドの認証システムと同期するセッションベースの認証システムを確保できます。

SEO

サーバーサイドレンダリングについての説明にあったように、検索エンジンはブラウザに対するサーバーのレスポンスを傍受し、ウェブページのHTMLコンテンツをインデックス化します。しかしSPAの場合、サーバーからはJavaScriptコンポーネントやJSONデータが返されるため、検索エンジンにとってページコンテンツの解釈が困難になります。

そこでInertiaは、アプリケーションに追加できるサーバーサイドレンダリング(SSR)機能を導入することで、この懸念を解決しています。Node.js環境をトリガーとして、JSONデータのレスポンスがHTMLに変換されます。

Inertiaがサーバーとブラウザ間に位置している様子を想像してみてください。サーバーがInertiaのリクエストを受信してJSONでレスポンスを返すと、InertiaはNode.jsサーバーの存在を検知してJSONレスポンスをHTMLに変換します。これにより、検索エンジンは(そのアプリがSPAでないかのように)ページをインデックス化できます。

また、InertiaではHeadコンポーネントにより、ページにタイトルとメタデータを追加することができます。


<script setup>
import { Head } from '@inertiajs/inertia-vue3'
</script>

<template>
  <Head>
    <title>Page Title</title>
    <meta name="description" content="Page Description" />
  </Head>
</template>

Inertiaのドキュメントにある別の例もご覧ください。


// Layout.vue

import { Head } from '@inertiajs/inertia-vue3'

<Head>
  <title>My app</title>
  <meta head-key="description" name="description" content="This is the default description" />
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</Head>

// About.vue

import { Head } from '@inertiajs/inertia-vue3'

<Head>
  <title>About - My app</title>
  <meta head-key="description" name="description" content="This is a page specific description" />
</Head>

フォームとフォームヘルパ

InertiaでHTMLを使った標準的なフォームのリクエストを送信することは可能です。しかし、それではページ全体が再読み込みされてしまいます。

そこでInertiaならではの方法を利用するのが便利です。Inertiaでのフォーム送信はサーバーサイドで処理されるため、ユーザーを同じページに戻す(または全く別のページに遷移する)リダイレクトを自由に行うことができます。

Inertiaでのフォームの操作は簡単です。Vue.js 3コンポジションAPIでの使用例を以下に示します。


<script setup>
  import { useForm } from "@inertiajs/inertia-vue3";

  const form = useForm({
    email: null,
    password: null,
  });
</script>

<template>
  <form @submit.prevent="form.post('kinsta/login')">
    <!-- email -->
    <input type="text" v-model="form.email" />
    <!-- password -->
    <input type="password" v-model="form.password" />
    <!-- submit -->
    <button type="submit">Login</button>
  </form>
</template>

GETPOSTPUTPATCHDELETEを使ったフォーム送信が可能です。


<script setup>
  import { useForm } from "@inertiajs/inertia-vue3";

  const form = useForm({
    email: null,
    password: null,
  });

  const submit = () => {
    form.post("kinsta/login");
  };
</script>

<template>
  <form @submit.prevent="submit()">
    <!-- email -->
    <input type="text" v-model="form.email" />
    <!-- password -->
    <input type="password" v-model="form.password" />
    <!-- submit -->
    <button type="submit">Login</button>
  </form>
</template>

Inertiaのフォームヘルパには、有用なプロパティが複数あります。例えば、processingプロパティは、フォームの処理が始まると、trueに変わります。これは、フォームの処理中に送信ボタンを無効にして、複数回の送信を防ぐのに使用できます。


<button type="submit" :disabled="form.processing">Submit</button>

また、preserveStatepreserveScroll、イベントコールバック各種も使用でき、フォームに他のオプションを追加する際に便利です。


form.post('kinsta/login, {
  preserveScroll: true,
  onSuccess: () => form.reset('password'),
})

Inertia.jsで状態を記憶する

あなたのサイトの訪問者がフォームに情報を入力したものの、それを送信することなく別のページに移動したとします。通常、この状況で、フォームのあるページに戻ってもただ空欄が表示されるのみです。

ここで注目したいのがInertiaのuseRememberという機能です。これを使えば、ユーザーの入力した情報をブラウザの履歴に保存し、後から復元することができます。

この機能は、Inertiaからインポートしてフォームに適用することで使用可能です。


import { useRemember } from '@inertiajs/inertia-vue3'

export default {
  setup() {
    const form = useRemember({
        first_name: null,
        last_name: null,
    })

    return { form }
  },
}

useRememberを使用するフォームがページに複数ある状況などでは、各コンポーネントにどのデータを復元するか識別できるように、それぞれ一意のキーを設定する必要があります。


const form = useRemember({
        first_name: null,
        last_name: null,
    }, 'Users/Create')

Inertiaのフォームヘルパを利用する際には、useRememberを使用する必要はありません。Inertiaでフォーム入力の状態が自動で記憶されるので、一意の識別子を与えるだけで大丈夫です。


import { useForm } from '@inertiajs/inertia-vue3'

const form = useForm('CreateUser', data)

この機能の長所として、アプリケーション内の任意のデータを手動で記憶できます。これは、深くネストされたコンポーネントからメインコンポーネントにデータを渡す際にも有用です。


import { Inertia } from '@inertiajs/inertia'

// DeeplyNestedComponent.vue
Inertia.remember(data, 'my-key')

// MainComponent.vue
let data = Inertia.restore('my-key')

ファイルのアップロード

Inertiaはフォームにファイルが含まれているかどうかを検出し、そうである場合にはリクエストデータをformDataオブジェクトに変換します。ちなみに、これは常に必要な処理となります。したがって、namaとavatarを含むフォームがある場合、Inertiaはそのフォームをmultipart/form-dataとして扱います。

バリデーションとエラー

ユーザーが不正な値を含むフォームを送信し、それが検証のためにサーバーに送信されると、検証エラーを伴うかたちで、そのユーザーはフォームページに遷移されます。またセッションからエラーが捕捉され、ページのpropsとして保存されます。

propsはリアクティブなので、フォームの送信が完了した時点での表示となります。エラーの存在を検出するために、page.props.errorsは常に監視されます。

エラーが見つかると、onSuccess()の代わりにonError()コールバックが出されます。

この挙動を理解するために、Vue 3を使用した例を以下に示します。


const submit = () => {
    form.post("kinsta/login", {
        onError: () => {
            return "Hi! , the server returned an error and Inertia saved it as a prop. Do as you like with me"
        },
        onSuccess: () => {
            return "Wohoo!!"
        }
    }
    );
  };

エラーの扱いは簡単です。propsとして定義し、HTMLに条件付きで表示します。


<script setup>
  defineProps({
    errors: Object,
  });

//
</script>

<template>
  <form @submit.prevent="submit()">
    //
    <div v-if="errors.email">{{ errors.email }}</div>
  </form>
</template>

Inertiaでは、エラー発生時に以前の入力データを気にする必要はありません。ユーザーがエラーページにリダイレクトされたことを検出すると、POSTPUTPATCHDELETEコンポーネントの以前の状態が自動で保存されます。

Inertia.jsによる部分的再読み込み

Inertiaの部分的再読み込みは非常に優れた機能です。サーバーからデータ全体を再度取得するのではなく、ページ上で選択されたコンポーネントのみを再読み込みします。これにより、アプリケーションの最適化を次のレベルに引き上げることが可能です。関連して、Laravelアプリケーションのパフォーマンスを最適化する方法もご覧ください。

部分的再読み込みは、Inertiaのonlyプロパティを使用して行うことができます。


import { Inertia } from '@inertiajs/inertia'

Inertia.visit(url, {
  only: ['users'],
})

Inertia.jsによる外部リダイレクト

サブドメインルーティング、または外部リダイレクトは、SPAにおける最も厄介な課題の1つです。アプリケーションをSPAとして保持しながら、別のドメインにアクセスするのはあまり合理的ではありません。

Inertiaのリクエストを外部のウェブサイトや、アプリ内のInertia以外のエンドポイントにリダイレクトすることが必要になる場合があります。これは、サーバーサイドから実行するwindow.locationにより実現可能です。


return Inertia::location($url);

このテスト中にコンソールを開くと、409 conflictが返されることがわかります。このレスポンスにはX-Inertia-LocationヘッダのURLが含まれており、Inertiaはこれをクライアント側で検出し、自動でリダイレクトを実行します。

Inertia.jsの扱い方

Inertiaでの作業は、完全にリアクティブなSPAであるという特徴を除いては、サーバーサイドアプリケーションのそれに似たものになります。ルートを定義し、コントローラをカスタマイズし、Inertiaがフロントエンドフレームワークに送信するビューを返すことができます。

LaravelルーティングとInertia.js

Laravelルーティングは、アプリケーション開発時に軽視してはならない要素です。これを使いこなすことで、複雑なルートを素早く構築できます。Laravelとそのルーティングの仕組みについての無料または有料のLaravel学習リソースは豊富に存在します。

Inertiaでは独自のルーティングシステムが採用されています。Laravelルーティングと連動しているので、単純なクライアントサイドルーティングを行うのにVue RouterやReact Routerを必要としません。ジョブの実行にバックエンドデータが必要でなければ、ルーターヘルパを使用して、コンポーネントに直接ルーティングできます。


Route::inertia('/home', 'HomeComponent');

public function index()
    {
    return Inertia::render('Users/Index', [
        'users' => User::all();
        'create_url' => URL::route('users.create'),
    ]);
}

ここで一旦、開発、デザイン、代行業を行う方々向けにお知らせです。シングルページや複数ページのWordPressウェブアプリを構築できる強力なツール、DevKinstaをご紹介します。便利な点として、WordPressはCorcelパッケージを使用してLaravelと統合することができます。また、WordPressを統合したLaravelアプリの構築などであれば、パフォーマンス監視にKinsta APMをご活用ください。

リダイレクト

リクエストに対応する正しいパスにリダイレクトすることは非常に重要です。例えば、ユーザーが投稿の送信にstoreエンドポイントを使用したとすると、投稿の完了後には、GETエンドポイントへのリダイレクトを行うといった配慮が必要になります。


public function store()
{
    Post::create(
        Request::validate([
            'title' => ['required'],
            'body' => ['required'],
        ])
    );

    // redirect the user to the posts show page
    return Redirect::route('posts.show');
}

Inertia.jsを使用する際の注意点

ここまでは、Inertiaを使用する利点に焦点を合わせてきました。しかし、Inertiaにも欠点があります。

  • VueやReactの基本的な知識が必要である
  • モデルデータは完全にクライアント側に渡すことができることから、関連するデータを明示的にフロントエンドに返すようにしなければならない
  • AndroidやiOSのネイティブアプリケーションを開発するとなった場合には、基本的にはAPIを作り直さなければならない

Inertia.jsを使うべきか

Inertiaを使うべきかどうかという質問に答えると、多くのケースでの活用をおすすめします。シングルページでサーバーサイドの、そして、SEOを考慮したモダンなアプリを構築したいのであれば、Inertiaは検討に値します。

Inertia.jsの公式サイトでドキュメントを確認することもおすすめします。

まとめ

ウェブ技術の進化に伴い、クライアントサイドのアプリやSPAの人気が高まりを見せています。一方で、従来型のサーバーサイドアプリケーションには衰退が感じられます。業界の変化に柔軟に対応できるように、最新の技術やツールを使えるようにしておくことが重要です。

Inertiaは、サーバーサイドに精通した開発者がシングルページアプリケーションを構築する上で、非常に便利なソリューションです。あらゆる問題を解決し、多くの時間を節約することができます。

Inertiaはサーバーサイドレンダリングをサポートしており、SEOを考慮した柔軟なSPAを構築することすら可能です。

Inertiaのコミュニティが活発であることも特筆に値します。Laravel Forge、Laracastsなど、有名どころがこれを支持しています。その結果、Inertiaは、Laravel開発者Laravel開発の需要が増え続ける中で、今後も継続的に改善・維持されることが期待されます。

また、Inertiaを使用したプロジェクトのホスティングサービスをお探しであれば、Kinstaのアプリケーションホスティングを是非ともご確認ください。