🚄

全力で爆速にするAstro.js

2024/06/19に公開

注意

この記事は、私が2024年4月頃に書いた記事の再編集です。情報が多少古い、もしくは検証しきれていない箇所がありますがご了承ください。

はじめに

皆さんはよくAstroでWeb制作をすると思います(圧)。
Astroでサイトを作ると特に対策せずとも爆速なサイトが出来上がりますが、それだけでは物足りずちゃんと対策して高速化を図る記事です。
すっごーい簡単な内容なのでご承知ください。

Lighthouse

実際にページのパフォーマンスを計測するにはよくLighthouseを使います。このサービスはGoogleがオープンソースで開発しているページインサイトツールです。馴染み深いですね。
これでサイトの計測をしている人が一番多いんじゃないでしょうか。
Chrome拡張機能をいれることで、オフライン環境であってもlocalhostサイトの計測ができます。
すでに公開済みのページなどでテストする場合はPageSpeed InsightsというWebアプリでテストするのが手軽です。これはLighthouseのWebアプリ版というだけで機能自体に差はありません。どちらでも同じようにサイトの確認作業が行えます。
サイトがある程度完成に近づいて次にやることはLighthouseで確認を行い、結果を見ながらサイトの改善です。
LighthouseではPerformance, Accessibility, Best Practices, SEO, PWAの5項目でサイトを分析してくれます。今回取り上げるのはサイトの速度であるPerformanceです。他4項目はこの記事では触れません。
一応サイトの計測時の注意点として、開発環境でテストしないでください。しっかりとビルドして本番環境を立ち上げてから計測しましょう。Astroの場合はnpm run buildを実行してからnpm run previewで本番環境を立ち上げることが可能です。

画像最適化

Astroでは自動的にsrcディレクトリ内にある画像がWebPに変換されます。
ざっくり書いてしまうとWebPはGoogleが開発しているとっても軽い画像形式です。そのためロード時間が早くなるメリットがあるので、Web業界では現在積極的に採用されている印象です。
この機能を使うためにはImageコンポーネントを使う必要があります。

---
import { Image } from "astro:assets";
import Logo from "../assets/logo.png";
---
<Image src={Logo} alt="ロゴ" />

このように書けば最適化されます。ただ、複雑な表示をしようとするとImageコンポーネントには拡張性が足りないと感じることがあります。

<img src={Logo.src} width={Logo.width} height={Logo.height} alt="ロゴ" >

imgタグを使うとHTMLと同じように画像を使用できます。この場合、最適化処理は弱くなるので、なるべくImageコンポーネントを使ったほうがいいでしょう。
ただ場合によってはWebPを使用したくない場合は、publicディレクトリに画像ファイルを置くことで、処理されずそのままの形式で使用できます。faviconなどはこっちに置いておきます。

フォント最適化

Webフォントには様々なものがありますが、この章で取り上げるのはGoogle Fontsです。Google Fontsを使う際には簡単に最適化処理することで、速度が上がります。

npm install astro-google-fonts-optimizer

こちらのツールはNext.jsのフォント最適化処理を模倣しています。
ビルド時に呼び出すフォントのCSSを取得してインライン化し、HTMLに直接埋め込むことでGoogle側からCSSを取得する時間をなくし最適化しているようです。

---
import { GoogleFontsOptimizer } from "astro-google-fonts-optimizer";
---
<GoogleFontsOptimizer url="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;500;700&display=swap" />

このように書くとビルド時にCSSがダウンロードされます。GoogleFontsOptimizerコンポーネントはheadタグに書いてください。
Google Fonts互換のBunny Fontsでも同じ最適化が可能ですが、今のところGoogle Fontsで事足りているので使っていません。

YouTube埋め込み

通常YouTubeを埋め込むためにはiframeタグを使用しますが、ロードに時間がかかります。軽量に読み込むためにlite-youtube-embedを使います。

npm install @astro-community/astro-embed-youtube

Astroでlite-youtube-embedを使うためのツールを使用します。

---
import { YouTube } from '@astro-community/astro-embed-youtube';
---
<YouTube id="AAAAAAA" />

仕組みとしては最初の時点ではYouTube動画を読み込まず、i.ytimg.comからサムネイルのみロードします。
ただ場合によってはサムネイル画像を同じサーバーに置きたい場合、いちいち手動で画像をダウンロードするのは非効率です。
AstroにはgetImageという関数があります。これを使うことでリモート画像をビルド時にダウンロードして_astroディレクトリに配置してくれます。

---
import { getImage } from "astro:assets";
const posterSource = await getImage({ src: posterURL, inferSize: true });
---
<lite-youtube style={`background-image:url('${posterImage.src}');`} />

このようなコードをastro-embed-youtubeに追加することでさらなる最適化が可能です。

アナリティクス

Googleアナリティクスや広告などの外部スクリプトを呼び出すとどうしてもサイトのパフォーマンスが下がってしまいます。Googleアナリティクスを埋め込んでLighthouseにて確認すると5点ほど下がったことが確認できました。

npx astro add partytown

Partytownでは読み込みたいスクリプトをメインスレッドではなく別スレッドで実行することでサイト自体の速度への影響をなくします。使い方は簡単でPartytownを使用したいscriptタグにtype="text/partytown"を追加し、astro.config.mjsに追加設定します。

partytown({
  config: {
    forward: ["dataLayer.push"],
  },
}),

dataLayer.push をwindowsオブジェクトに転送することで動作するようにしています。
Googleアナリティクスはグローバルのwindowオブジェクトにアクセスする必要があるため、この設定をしないと正しく動作しません。

ファイル圧縮

アクセスする際にサーバーから受け取るファイルサイズは小さいほど、通信量が減りロード時間が短くなります。

npm run astro add @playform/compress

ここで初めて出てきましたが、このastroコマンドはパッケージのインストールとastro.config.mjsなどへの追記を対話形式でしてくれます。複雑な設定を簡単にしてくれるのでとても便利です。
このツールを使用するだけで自動的にビルドファイルが圧縮されます。実際にテストプロジェクトを圧縮してみました。

圧縮なし 圧縮あり
786 KB 164 KB

表1のように1/4以上に圧縮できていることがわかります。コードから改行を無くすなど様々な処理して、CSS、HTML、JavaScript、画像を自動で圧縮するようになっています。
この記事では説明不足なのでより詳しい@playform/compressのGitHubを見てください。画像の圧縮に関してはAstro自体にも備わっていると先ほど説明しました。Astroの機能とは違い@playform/compressではpublicフォルダの中の画像に対しても圧縮が行われます。もちろんファイル形式自体は変換されないので安心してください。

CSSインライン化

WebサイトにおいてCSSは最初に読み込んでおくことが普通です。それはCSSの適用されていないページを一瞬ユーザーに見せてからサイトの見た目が変わることを防ぐためですが、実際にはユーザーから見えない場所のCSSまでロードしてしまいます。

npm install -D -E @playform/inline

このパッケージは新しいため、先ほど紹介したastroコマンドに対応していないため、astro.config.mjsにコードを書く必要があります。

import playformInline from "@playform/inline";
export default defineConfig({
  integrations: [playformInline()],
});

この処理にはCrittersが使用されています。特別、設定を加える必要はありません。
ただ実際に私の運営するホームページに適用すると本番環境でのみ、Webフォントがロードされない問題が発生しました。原因は今のところ不明ですが先ほど紹介したGoogleフォント最適化のGoogleFontsOptimizerと相性が悪いのかもしれません。
READMEによるとCrittersの設定をそのまま使用可能とのことで、fontsをfalseに設定してフォントのみインライン化処理を無効化できます。

playformInline({ Critters: { fonts: false }})

こうすればフォントのインライン化がされないはずなのですが、やはりうまくいきませんでした。
フォントを取るかCSSを取るか考えたとき、
原因究明でき次第、Crittersの使用を再開しようと考えています。

おわりに

以上!

Discussion