Next.jsでSSRを使わずに画像最適化と多言語化をしてみた
やりたかったこと
画像の最適化
このWebサイトでは、ゲームのサムネイルにgifアニメを使っておりました。
gifアニメは昔からある非常に便利なものなのですが、容量が大きくなりがちな欠点があり、ページの初回読込速度がかなり遅くなりました。
そのため、サムネイルにgifアニメではない別の仕組みを使いたくなりました。
多言語化
従来は日本語と英語を同じ場所に併記しておりました。
しかし、それだと見栄えが悪いだけではなく、メタタグを言語ごとに切り替えられないなどSEO対策の面でも良くないです。
Googleが推奨するように、日本語と英語は別URLの別ページとして管理したくなりました。
最初に思い付いた解決策
最初に思い付いた解決策としては、
- 画像の最適化 → Next/Imageを使う
- 多言語化 → Next.js標準の仕組みやnext-i18nextなどのライブラリを使う
というのがありました。
しかし、どちらもSSRを前提としておりました。
当サイトはSSGで静的サイトとしてエクスポートしていたので、SSRに移行しなければこれらの機能を使用することができません。
SSRではいけないのか
SSRを使おうとすると、サーバーの維持や管理にコストがかかります。
なお、私はホスティングサービスにCloudflare Pagesを使っているのですが、最近Cloudflare PagesにNext.jsのWebサイトをデプロイするとSSRが動作するようになりました。
しかし、Edge RuntimeではfsのようなNode.jsのモジュールを使うことができません。
私はブログを自作する際にfsを使ったため、これは致命的でした。
さらに、Cloudflare PagesだとNext/Imageも本記事の執筆時点ではうまく機能しないようです。
したがって、SSGからSSRに移行することは難しく、別の方法を探すことになりました。
SSRを使わない解決策
画像最適化
SSGで画像を最適化するためのライブラリもありましたが、Next 13に未対応だったので、手動で画像を最適化しました。
特に重いのはGifアニメだったのですが、これをWebM形式に変換することで対応しました。
WebMとは
WebMとは、2010年からGoogleが提供しているオープンでロイヤリティーフリーな動画ファイル形式のことです。
その名の通りWebに親和的なフォーマットで、容量に対して画質が非常に良いです。
FFmpegでGifアニメをWebMに変換することで、なんとファイル容量が90%以上削減されました!
WebM形式の動画をGifアニメのように自動でループ再生させるためには、以下のようにvideo
タグにautoPlay loop muted playsInline
属性を指定します。
<video autoPlay loop muted playsInline>
<source src={props.data.imgSrc.webm} type="video/webm" />
<source src={props.data.imgSrc.mp4} type="video/mp4" />
</video>
autoPlay
やplaysInline
などといったようにHTMLの属性名ではなくキャメルケースで記述しています。JSXではない生のHTMLでは
autoplay
やplaysinline
のように属性名を指定してください。autoPlay
だけでは自動再生されず、muted
も必要です。さらに、iOSでは
playsInline
も指定しないと動画が画面全体で開かれてしまいます。2つ目のsource
にはmp4ファイルを指定しています。
これにより、WebM非対応のブラウザではmp4が再生されます。
しかし、モダンブラウザのうちSafariではつい最近までWebM非対応でしたが、本記事の執筆時点では対応しているため、WebMだけでいいかもしれません。
Gif以外の画像の最適化
私はまだ未実施ですが、jpg形式やpng形式の画像をWebP形式に変換することで大幅な容量の削減が可能になります。
Next/Imageのように画面サイズに合わせて画像のサイズを調整するレベルまで最適化するとなると難しいですが、これだけでもかなりパフォーマンスが改善するはずです。
多言語化
SSGでもi18n(国際化(internationalization)の意。iとnの間に18文字あることからi18nと略す。)をすることができるライブラリがあります。
このライブラリではhttps://hogefuga.com/?lang=en
といった具合にURLにクエリを付けることで対応する仕組みとなっています。
しかし、この方法はSEO対策の観点からはGoogleにより非推奨とされています。
SEO対策を気にしなければこの方法でも良いのですが、私は海外の方にもこのWebサイトに来ていただきたいので、https://hogefuga.com/en
のようにサブディレクトリの別ページとして英語版のページを持たせる必要があります。
具体的な方法
このWebサイトの構造はそこまで複雑ではないので、ライブラリを使わずに自力でi18n対応をしました。
具体的には、「ある日本語のページに対応する英語のページは、en
というサブディレクトリで管理する」というルールにしました。
また、現在選択されている言語はuseRouter
のasPath
を使って現在のディレクトリ名がen
かどうかで判定する方式とし、hrefでの参照先には現在の言語と同じページを参照する方式としました。
最後に、言語切り替えボタンですが、
- 現在のディレクトリ名が
en
のとき(英語のとき)
一つ上のディレクトリ(つまり日本語ページ)に移動する - 現在のディレクトリ名が
en
でないとき(日本語のとき)
en
サブディレクトリ(つまり英語ページ)に移動する
という具合に実装しました。
最後に
Next.jsにはNext/Imageやi18nなど標準で便利な機能が組み込まれていますが、Next.jsをSSGとして利用するとなると途端に難しくなる印象でした。
SSGでこれらの機能を実現するライブラリもなくはないですが、Webサイトの構造がそこまで複雑ではないのなら、下手に依存関係を増やすよりも自力で実装した方がかえって楽でした。
今回はあくまでもNext.jsで対応するという趣旨でしたが、SSGとしての機能に特化したGatsby.jsを使うなどの方法も考えられるので、機会があればそちらも記事にしてみたいです。