🚀

webサイトをAstroにリライトしてパフォーマンスが上がった話

2023/05/15に公開1

4年ほど前に作ったwebサイトをリニューアルしました。

リニューアル前 リニューアル後
スクリーンショット 画像:リニューアル前のwebサイトスクリーンショット 画像:リニューアル後のwebサイトスクリーンショット
Lighthouse 画像:リニューアル前のLighthouseスコア 画像:リニューアル後のLighthouseスコア

デザインはほぼ同じですが、完全にリライトしています。Lighthouseの記録もリニューアル後にかなり向上したことがわかります。

トップに画像のカルーセルがあるようなサイトでのこのスコアなので個人的は満足しています。

この記事ではこのサイトリニューアルに際しておこなったことを紹介していきます。

NetlifyからCloudflare Pagesにホスティング先を変更

Netlify(無料プラン)で配信するとレスポンスにかかる時間が他のサービスに比べて長いです。

そこで、Vercel、Cloudflare Pages, Amplify hostingなどを比較し、レスポンスまでの時間が早く、料金体系が良心的なCloudflare Pagesに移行しました。

以下はリニューアル後のサイトをNetlifyにデプロイしたものと、Cloudflare PagesにデプロイしたものをDevtoolsのパフォーマンスタブで計測し、比較したものです。

Netlifyにデプロイ Cloudflare Pagesにデプロイ
500msまでのcpu使用率 画像:Netlifyでのcpu使用率 画像:Cloudflare Pagesでのcpu使用率
First Paint 画像:NetlifyでのFirstPaint 画像:Cloudflare PagesでのFirstPaint

一番上の画像はパフォーマンスタブで計測した際に一番上に表示されるCPU使用率のタイムラインから最初の500msまでを抜き出したものです。CPU使用率が上がるというのは、HTMLをパースし、レイアウトを計算し...といったサイト表示に関する処理が走り始めているということになります。

NetlifyとCloudflare Pagesの方を比較してみると、Netlifyの方では150~160ms経ってからCPU使用率が上がり始めているのに対して、Cloudflare Pagesの方では大体45~50msぐらいからCPU使用率が上がっているのでCPUが忙しくなるタイミングがNetlifyにデプロイしたサイトの方が100msほど遅いことがわかります。

次に、FirstPaintというのはページのレンダリングが開始された時間のことです。
こちらもNetlifyとCloudflare Pagesで140msほどの差がありNetlifyにデプロイした方がレンダリング処理が始まるのが140msほど遅いことがわかります。

このようにCloudflare Pagesにデプロイすることでサイトパフォーマンスが向上したことがわかります。
Jamstackアーキテクチャではポータビリティは特徴のひとつであり、デプロイ先の変更は難しいことではないのでコスパの高い施策だと思います。

画像の最適化

リニューアル前のサイトではできていなかった画像の最適化にも取り組み、使われている写真などは全てwebpフォーマットにしました。webpよりもファイル容量が抑えれられるavif形式は、edgeがいまだに未対応です。

また、それぞれのコンポーネントに合わせた適切な寸法、圧縮率で画像を読み込み容量を減らしました。

webp画像は一枚一枚編集ソフトなどを用いて自身で変換したわけではなく、AstroのAssets機能、microCMSのクエリで用意しました。

AstroのAssets機能

Astroのv2.1から使える機能でローカルにおいた画像からフォーマット、幅、圧縮率を調節した画像を生成できるコンポーネントや関数があります。
カルーセル画像や、店内画像など、性質的にローカルに置いておきたい画像などはこの機能でwebp画像を用意しました。

microCMSの画像API

その他の商品データは画像を含め、全てmicroCMSにあるので、画像URLにfm=webpとパラメータを付与するだけでwebp画像を読み込めました。コンポーネントごとに必要なサイズが違うのに対してもそれぞれのコンポーネントでw=200などパラメータを変えるだけなので楽に用意できました。

loading属性とdecoding属性の指定

imgタグのloading属性とdecoding属性も適切に設定しました。
loading="lazy" decoding="async"とすればブラウザに画像を読み込む優先順位が低いことを伝えられ、メインスレッドの処理を阻害せずに画像を読み込めます。

逆に、トップにあるカルーセルの画像などはアクセスした際に最初からviewportに入っているので優先順位が高く、遅延読み込みをするとかえってパフォーマンスの低下につながります。

ですので、そのような画像についてはloading="eager" decoding="sync"を指定し優先順位が高いことをブラウザに伝えています。

Nuxt.jsからAstroへ

このサイトは元々Nuxt.jsで作られていました。それから知識も増え、考え方も変わりました。このサイトのようにアプリのような要素の少ない、いわゆるwebサイトでは、Astroが最適だと思ったので乗り換えました。

Astroでは動的な部分と静的な部分を明確に区別しランタイムに走る処理を小さく、また、走るタイミングを制御できます。これにより特に難しいことを考えなくとも普通に作るだけでかなりパフォーマンスの良いサイトができます。今回のリニューアルによるパフォーマンスの改善も大部分がAstroのおかげです。

JavaScriptが必要な処理はSvelteで

カルーセルや、モーダル、ハンバーガーメニュー、viewportに入った際の文字のアニメーションなどクライアントでjsを使った動的な動作が必要な処理はSvelteで書きました。

リニューアル前はカルーセルを実現するライブラリ、アニメーションのライブラリなどを入れていましたが、Svelteがアニメーションやトランジションの機能などを全部持っているのと、僕のjs力の向上もあり、そのようなライブラリは使わずに済みました。

astro-islandコンポーネントが生成される場所に注意

Astroでは、コンポーネントにclient:loadというようなディレクティブを付与することで、そのコンポーネントがハイドレーションされるタイミングを制御します。

clientディレクティブを付与すると、そのコンポーネントは<astro-island>というwebコンポーネントに囲われます。ですので、例えばli要素にclientディレクティブを付与した場合、li要素が<astro-island>に囲われて、ul要素の子要素にli要素以外が入っていることをLighthouseの怒られる、というようなことがあります。

このような最終的なDOMの構造にも注意して、どこまでを動的に、どこまでを静的にするか考える必要があります。

まとめ

書きたいことをつらつら書いたら話が散らかりましたが、コンテンツ重視のサイトにAstroはおすすめの選択肢です。

リニューアル後のwebサイトもよかったら見てやってください。

Discussion

tommy34tommy34

リニューアル前後でアクセス速度が何msから何msになったのか気になります。