進捗インジケーター

Inertia のリクエストは XHR 経由で行われるため、通常、ページ間を移動してもブラウザの読み込みインジケーターは表示されません。これを解決するため、Inertia では Inertia 訪問を行うたびに、ページ上部に進捗インジケーターを表示します。ただし、非同期リクエスト は、明示的に設定しない限り進捗インジケーターを表示しません。

もちろん、必要であれば Inertia のデフォルトの読み込みインジケーターを無効にし、独自のカスタム実装を提供することもできます。以下では、両方のアプローチについて説明します。

デフォルト

Inertia のデフォルトの進捗インジケーターは、NProgress ライブラリを軽量にラップしたものです。createInertiaApp() 関数の progress プロパティを通じてカスタマイズできます。

js
createInertiaApp({
    progress: {
        // 進捗バーが表示されるまでの遅延(ミリ秒)...
        delay: 250,
        // 進捗バーの色...
        color: '#29d',
        // デフォルトの NProgress スタイルを含めるかどうか...
        includeCSS: true,
        // NProgress のスピナーを表示するかどうか...
        showSpinner: false,
    },
    // ...
})

progress プロパティを false に設定することで、Inertia のデフォルトの読み込みインジケーターを無効にできます。

js
createInertiaApp({
    progress: false,
    // ...
})

プログラムによる操作

Inertia リクエスト以外で進捗インジケーターを制御する必要がある場合、たとえば Axios や他のライブラリでリクエストを行うときは、Inertia の進捗メソッドを直接使用できます。

hide()reveal() メソッドは、コードの別々の部分が進捗の表示・非表示を制御する必要がある場合に、競合を防ぐために連携して動作します。hide() が呼ばれるたびに内部カウンターがインクリメントされ、reveal() でデクリメントされます。このカウンターがゼロになったときにのみ、進捗バーが表示されます。

ただし、reveal() にはこのカウンターを無視するオプションの force パラメータがあります。Inertia は内部的にこの仕組みを使い、プリフェッチ中は進捗を非表示にしつつ、実際のナビゲーション時には確実に表示されるようにしています。

js
progress.hide()    // カウンター = 1、バーは非表示
progress.hide()    // カウンター = 2、引き続き非表示
progress.reveal()  // カウンター = 1、引き続き非表示
progress.reveal()  // カウンター = 0、バーが表示される

// force を指定するとカウンターを無視して表示
progress.reveal(true)

createInertiaApp()progress: false を設定して進捗インジケーターを無効にしている場合、これらのプログラムによるメソッドは動作しません。

カスタム

Inertia のイベントを使用して、独自のページ読み込みインジケーターを設定することも可能です。ここでは例として NProgress ライブラリを使った方法を見ていきます。

まず、Inertia のデフォルトの読み込みインジケーターを無効にします。

js
createInertiaApp({
    progress: false,
    // ...
})

次に、NProgress ライブラリをインストールします。

bash
npm install nprogress

インストール後、NProgress のスタイルをプロジェクトに追加する必要があります。CDN でホストされているスタイルを使用できます。

html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" />

次に、NProgress と Inertia の router の両方をアプリケーションにインポートします。

次に、start イベントリスナーを追加します。このリスナーは、新しい Inertia 訪問が開始されたときに進捗バーを表示するために使用します。

js
router.on('start', () => NProgress.start())

最後に、ページ訪問が完了したときに進捗バーを非表示にするための finish イベントリスナーを追加します。

js
router.on('finish', () => NProgress.done())

これで完了です!ページ間を移動すると、進捗バーがページに追加され、削除されるようになります。

キャンセルされた訪問の処理

このカスタム進捗実装は、正常に完了するページ訪問には最適ですが、キャンセルされた訪問も処理できると便利です。まず、(新しい訪問によってキャンセルされた)中断された訪問では、進捗バーを単純に開始位置にリセットします。次に、手動でキャンセルされた訪問では、進捗バーを即座にページから削除します。

これは、finish イベントに提供される event.detail.visit オブジェクトを確認することで実現できます。

js
router.on('finish', (event) => {
    if (event.detail.visit.completed) {
        NProgress.done()
    } else if (event.detail.visit.interrupted) {
        NProgress.set(0)
    } else if (event.detail.visit.cancelled) {
        NProgress.done()
        NProgress.remove()
    }
})

ファイルアップロードの進捗

さらに一歩進めてみましょう。ファイルをアップロードしている間、アップロードの進捗を反映するように読み込みインジケーターを更新できると便利です。これは progress イベントを使用して実現できます。

js
router.on('progress', (event) => {
    if (event.detail.progress.percentage) {
        NProgress.set((event.detail.progress.percentage / 100) * 0.9)
    }
})

これで、ファイルのアップロード中に進捗バーが「徐々に進む」だけでなく、リクエストの進捗に基づいて実際に位置が更新されるようになります。ここでは進捗を 90% に制限しています。これは、サーバーからのレスポンスをまだ待つ必要があるためです。

読み込みインジケーターの遅延

最後に実装するのは、読み込みインジケーターの遅延です。リクエストが 250〜500 ミリ秒以上かかった場合にのみ、読み込みインジケーターを表示する方が望ましいことがよくあります。これにより、素早いページ遷移のたびにインジケーターが表示されるのを防ぎ、視覚的な煩わしさを軽減できます。

この遅延動作を実装するために、setTimeoutclearTimeout 関数を使用します。まず、タイムアウトを追跡するための変数を定義します。

js
let timeout = null

次に、start イベントリスナーを更新し、250 ミリ秒後に進捗バーを表示する新しいタイムアウトを開始します。

js
router.on('start', () => {
    timeout = setTimeout(() => NProgress.start(), 250)
})

次に、ページ訪問がタイムアウトより先に終了した場合に、既存のタイムアウトをクリアするため、finish イベントリスナーを更新します。

js
router.on('finish', (event) => {
    clearTimeout(timeout)
    // ...
})

finish イベントリスナー内では、進捗バーが実際に表示を開始しているかどうかを判定する必要があります。そうしないと、タイムアウトが完了する前に誤って表示されてしまいます。

js
router.on('finish', (event) => {
    clearTimeout(timeout)

    if (!NProgress.isStarted()) {
        return
    }
    // ...
})

最後に、progress イベントリスナーでも同じチェックを行います。

js
router.on('progress', event => {
    if (!NProgress.isStarted()) {
        return
    }
    // ...
})

これで、美しいカスタムページ読み込みインジケーターの完成です!

完全な例

参考までに、カスタム読み込みインジケーターの最終版の完全なソースコードを以下に示します。

訪問オプション

これらの設定に加えて、Inertia.js にはリクエストごとに読み込みインジケーターを制御するための 2 つの訪問オプション showProgressasync が用意されています。これらのオプションにより、非同期リクエストの扱いや進捗インジケーターの管理をより細かく制御できます。

Showprogress

showProgress オプションは、リクエスト中の読み込みインジケーターの表示・非表示を細かく制御するためのものです。

js
router.get('/settings', {}, { showProgress: false })

Async

async オプションを使用すると、デフォルトの進捗インジケーターを表示せずに非同期リクエストを実行できます。showProgress オプションと組み合わせて使用することも可能です。

js
// 進捗インジケーターを無効化
router.get('/settings', {}, { async: true })
// 非同期リクエストで進捗インジケーターを有効化
router.get('/settings', {}, { async: true, showProgress: true })