reading and coding ...

Next.js の Route Masking

8/26/2019, 3:25:10 PM - 269 days ago

Next.js の <Link> タグには hrefas という2つの属性があり、この区別は Next.js を静的サイトビルダーとして使う場合であっても重要であることに今日ようやく気づきました。その知見を記しておきます。


Next.js はデフォルトでは pages ディレクトリ配下に置かれたファイルの構成に従い、ファイル名と同名のページで Routing を行います。
この挙動をカスタマイズするためには、next.config.js というファイルを編集し、独自の exportPathMap を定義してやればよいです。
exportPathMap は、pages ディレクトリ内のファイル名と実際に Routing を行う時のページ名を対応づける連想配列で、このブログでも以前から、例えば pages ディレクトリ内の /posts/20190217.mdx (内部的には /posts/20190217.js)を、実際には /blog/2019/02/17 という名前で Routing させていました (年・月・日が区切られていて見やすいですからね)。
こうすることによって、 next build で静的サイトとしてビルドする際も、/posts/20190217.mdx の内容が /blog/2019/02/17.html として書き出されることになります。
私はこの挙動でずっと問題ないと考えていました。


昨日、深夜にお酒を飲みつつ、なんとなくデベロッパーツールを見ながら /blog ページを開いていた私は、コンソールに多量のエラーが表示されていることに気づき、驚きました。
原因は /blog/2019/02/17.js のような、ブログ記事のリンク名と同名の js を GET しようとしていることにありました。
/blog ページには、それぞれの post へのリンクが <Link> で置かれています。Next.js の <Link> はデフォルトで prefetch を行い、バックグラウンドでリンク先を読み込もうとしてくれるのですが、その prefetch が失敗してエラーを起こしていたわけです。
よく考えれば、実際にあるのは /blog/2019/02/17.js ではなく /posts/20190217.js のはずです。そこで私は next.config.js の編集だけでなく、クライアントサイドの Dynamic Routing にも気を配らねばならなかったことにようやく気づきました。


ここで大切になってくるのが <Link> タグの hrefas の違いです。
Next.js のドキュメントを引用すると、

  • href: the path inside pages directory
  • as: the path used by your server routes

です。
例えば今回の場合では、/posts/20190217.js から生成される内容を、/blog/2019/02/17 として Routing させるので、<Link href="/posts/20190217" as="/blog/2019/02/17"> が正しかったのです。
すぐに修正コミットを打ちました。

この結果、 /blog ページからそれぞれの post へのリンクを踏んで表示させるのに必要な時間が目に見えて短縮されました。prefetch が正しく行われるようになったからです。
実際のファイル名とは別の名前のページに Routing させる Route Masking の技術が肝要でした。


今まで Next.js の <Link> タグの as は何となく存在している程度にしか思っておらず、今回のような劇的な改善の可能性を見過ごしていたことについては、恥じ入るばかりです。
そもそも何故これまでコンソールエラーに気づけなかったのかを調べると、どうやらエラーが明示されるようになったのは Next.js が 9 系のバージョンに上がってからのようでした。やはり、ツールを最新バージョンにアップデートしていくのは非常に重要なことだと思います。