きっかけ
ReactやNext.jsの普及により、SPAのサイトも増えてきましたよね。
一般的なSPAでは、ページ遷移時にリンクのクリックをJavaScriptで検知し、DOMの再描画やHistory APIを用いたURLの書き換えなどを行うことが多いと思います。
また、処理に時間がかかる場合は、DOM上にスピナーやプログレスバーを表示するのが一般的だと考えていました。
しかし、Amaz○n Prime Videoでリンクをクリックしたところ、SPAであるにもかかわらず、プログレスバーだけでなくブラウザのタブも読み込み中の表示(ぐるぐる)になっていることに気づきました。
この挙動が気になって調べてみたところ、「Navigation API」によって実現できることが分かりました。
Navigation APIとは?
MDNによると、Navigation APIは、ナビゲーションアクション(ページ移動に関わる操作)を開始・傍受・管理する機能を提供するAPIです。History APIの後継的な位置づけで、SPAのニーズに特化して設計されています。
2021年頃から「App History API」として開発が進められていました。
使ってみよう
実際に使ってみましょう。
<a href="/fuga">hoge</a>
navigation.addEventListener("navigate", (event) => {
if (!event.canIntercept) return;
const url = new URL(event.destination.url);
event.intercept({
handler() {
// 本来はここにページ遷移時の処理を書く
alert(url.pathname);
}
});
});
「hoge」というリンクを押すとURLが更新され、「/fuga」とアラートが表示されていることが分かります。また、戻るボタンを押した場合などにも処理が実行されます。
ページ移動が発生するとnavigateイベントが発火し、event.intercept()を実行することで実際のページ遷移を防ぐことができます(event.preventDefault()に近いイメージです)。
従来の方法と比べて、次のような利点があります。
- aタグのクリックイベントを個別に処理しなくてよい
- 戻る・進む操作への対応を個別に実装しなくてよい
- History APIの
pushStateを直接扱わなくてよい
ぐるぐる
では本題の「ブラウザのタブをぐるぐるさせる」挙動は、どのように実現されているのでしょうか。
答えはシンプルで、event.intercept()に渡すhandlerを非同期関数にするだけです。
試しに、3秒待つ処理を入れてみましょう。
navigation.addEventListener("navigate", (event) => {
if (!event.canIntercept) return;
const url = new URL(event.destination.url);
event.intercept({
async handler() {
await new Promise((resolve) => {
setTimeout(resolve, 3000);
});
alert("遷移完了: " + url.pathname);
}
});
});
おめでとうございます 🎉
これで、リンクをクリックするとブラウザのタブが読み込み中の表示になり、3秒後に処理が完了します。
SPAでも、ネイティブのページ遷移のような読み込み表示を実現できました。
まとめ
Navigation APIを使うことで、SPAでもブラウザネイティブに近いナビゲーション体験を実現できます。
ただし現時点ではReactなどの主要なフレームワークが標準でこのAPIを活用しているわけではないため、意識しないと触れる機会は少ないかもしれません。
