InertiaのリクエストはXHR経由で行われるため、通常、ページからページへのナビゲーション時にはブラウザのローディングインジケーターは表示されません。これを解決するために、InertiaはInertiaの訪問を行うたびにページの最上部にプログレスインジケーターを表示します。
もちろん、ご希望であれば、Inertiaのデフォルトのローディングインジケーターを無効にして、独自のカスタム実装を提供することもできます。以下では、両方のアプローチについて説明します。
Inertiaのデフォルトのプログレスインジケーターは、 NProgressライブラリの軽量ラッパーです。これは、createInertiaApp()
関数のprogress
プロパティを使用してカスタマイズできます。 createInertiaApp()
関数のprogress
プロパティを設定することで、Inertiaのデフォルトのローディングインジケーターを無効にすることができます。
createInertiaApp({
progress: {
// The delay after which the progress bar will appear, in milliseconds...
delay: 250,
// The color of the progress bar...
color: '#29d',
// Whether to include the default NProgress styles...
includeCSS: true,
// Whether the NProgress spinner will be shown...
showSpinner: false,
},
// ...
})
Inertiaのデフォルトのローディングインジケーターを無効にするには、progress
プロパティを false
.
createInertiaApp({
progress: false,
// ...
})
Inertiaのイベントを使用して、独自のカスタムページローディングインジケーターを設定することもできます。例として、NProgressライブラリを使用してこれを実現する方法を探ってみましょう。
まず、Inertiaのデフォルトのローディングインジケーターを無効にします。
createInertiaApp({
progress: false,
// ...
})
次に、NProgressライブラリをインストールします。
npm install nprogress
インストール後、NProgressの スタイルをプロジェクトに追加する必要があります。これは、CDNでホストされているスタイルのコピーを使用して行うことができます。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" />
次に、NProgress
とInertiaのrouter
の両方をアプリケーションにインポートします。
import NProgress from 'nprogress'
import { router } from '@inertiajs/vue3'
次に、start
イベントリスナーを追加しましょう。このリスナーを使用して、新しいInertia訪問が開始されたときにプログレスバーを表示します。
router.on('start', () => NProgress.start())
次に、ページの訪問が終了したときにプログレスバーを非表示にするために、finish
イベントリスナーを追加しましょう。
router.on('finish', () => NProgress.done())
以上です!これで、ページからページへ移動するにつれて、プログレスバーがページに追加および削除されるようになります。
このカスタムプログレス実装は、正常に完了するページ訪問には最適ですが、キャンセルされた訪問も処理できるとさらに便利です。まず、中断された訪問(新しい訪問の結果としてキャンセルされたもの)の場合、プログレスバーは開始位置にリセットする必要があります。次に、手動でキャンセルされた訪問の場合、プログレスバーはページからすぐに削除する必要があります。
これは、finishイベントに提供されるevent.detail.visit
オブジェクトを調べることで実現できます。
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
イベントを使用して行うことができます。
router.on('progress', (event) => {
if (event.detail.progress.percentage) {
NProgress.set((event.detail.progress.percentage / 100) * 0.9)
}
})
これで、ファイルのアップロード中にプログレスバーが「じわじわと」進むのではなく、リクエストの進捗状況に基づいて実際に位置が更新されるようになります。サーバーからの応答を待つ必要があるため、ここでは進捗状況を90%に制限しています。
最後に実装するのは、ローディングインジケーターの遅延です。リクエストが250〜500ミリ秒以上かかって初めてローディングインジケーターを表示することが望ましい場合がよくあります。これにより、ページ訪問が高速な場合にローディングインジケーターが常に表示されるのを防ぎ、視覚的に気を散らすのを防ぎます。
遅延動作を実装するには、setTimeout
関数とclearTimeout
関数を使用します。まず、タイムアウトを追跡するための変数を定義することから始めましょう。
let timeout = null
次に、start
イベントリスナーを更新して、250ミリ秒後にプログレスバーを表示する新しいタイムアウトを開始しましょう。
router.on('start', () => {
timeout = setTimeout(() => NProgress.start(), 250)
})
次に、ページの訪問がタイムアウト前に終了した場合に、既存のタイムアウトをクリアするために、finish
イベントリスナーを更新します。
router.on('finish', (event) => {
clearTimeout(timeout)
// ...
})
finish
イベントリスナーでは、プログレスバーが実際に進捗状況の表示を開始したかどうかを判断する必要があります。そうしないと、タイムアウトが終了する前に誤ってプログレスバーが表示されてしまいます。
router.on('finish', (event) => {
clearTimeout(timeout)
if (!NProgress.isStarted()) {
return
}
// ...
})
そして、最後に、progress
イベントリスナーでも同じチェックを行う必要があります。
router.on('progress', event => {
if (!NProgress.isStarted()) {
return
}
// ...
}
これで、美しいカスタムページローディングインジケーターが完成しました!
便宜上、カスタムローディングインジケーターの最終バージョンの完全なソースコードを以下に示します。
import NProgress from 'nprogress'
import { router } from '@inertiajs/vue3'
let timeout = null
router.on('start', () => {
timeout = setTimeout(() => NProgress.start(), 250)
})
router.on('progress', (event) => {
if (NProgress.isStarted() && event.detail.progress.percentage) {
NProgress.set((event.detail.progress.percentage / 100) * 0.9)
}
})
router.on('finish', (event) => {
clearTimeout(timeout)
if (!NProgress.isStarted()) {
return
} else 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()
}
})