AstroとNext.jsのSSGを比較する
コンテンツ主体のWebサイトをJamstackで作成する場合のStatic Site GeneratorとしてAstroとNext.jsの2つを比較してみる。
- どちらもミニマムなページ + 1000ページのサイトで比較する。
- Next.jsはStatic Generationを利用する。
準備編
まず、Headless CMSの代わりとして、簡単なAPIを用意する。
Honoを使ってCloudflare Workersに公開する。(めちゃくちゃ簡単だった)
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => {
return c.text('Hello!');
});
app.get('/:id', (c) => {
const id = c.req.param('id');
const body = Math.random().toString(32).substring(2);
return c.json({ id, body });
});
export default app;
https://dummy-data-api.xxxxx.workers.dev/1
にアクセスすると次のようなレスポンスが返ってくるようになった。
{"id":"1","body":"q142g79884"}
Next.jsバージョン。 getStaticProps
の中でAPIを叩いてデータを取得する。
import { GetStaticPaths, GetStaticProps } from "next";
import Head from 'next/head';
interface Props {
id: number;
body: string;
}
export default function Page({ id, body }: Props) {
return (
<>
<Head>
<title>{body}</title>
</Head>
<small>Next.js version</small>
<h1>id: {id}</h1>
<h2>body: {body}</h2>
</>
)
}
export const getStaticPaths: GetStaticPaths = async () => {
// 1000ページ分のpathを用意
const paths = [...Array(1000).keys()].map((i) => {
return {
params: { id: (i + 1).toString() }
}
});
return { paths, fallback: false }
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
const response = await fetch(`https://dummy-data-api.xxxxx.workers.dev/${params?.id}`);
const { id, body } = await response.json();
return { props: { id: data?.id, body: data?.body } }
}
Astroバージョン。frontmatterの中でAPIを叩いてデータを取得する。frontmatterの中はすべてビルド時にのみ実行されるため、考えることが少なく楽に書ける印象。getStaticPaths
はNext.jsとほぼ同じ。
---
import Layout from "../../layouts/Layout.astro";
export function getStaticPaths() {
return [[...Array(1000).keys()].map((i) => {
return {
params: { id: i + 1 }
}
})]
}
const { id } = Astro.params;
const response = await fetch(`https://dummy-data-api.xxxxx.workers.dev/${id}`);
const { body } = await response.json();
---
<Layout title={body || 'Not found'}>
<small>Astro version</small>
<h1>id: {id}</h1>
<h2>body: {body || 'Not found'}</h2>
</Layout>
ビルドして http://localhost:3000/posts/1
にアクセスするとそれぞれ次のようなページができあがる。
Next.js | Astro |
---|---|
ビルド時間の比較
hyperfineを使ってそれぞれのビルド処理のベンチマークを計測してみた。
ビルドはNext.jsのほうが圧倒的に早い。2倍の差が出た。
Next.js
❯ hyperfine 'npm run build'
Benchmark 1: npm run build
Time (mean ± σ): 9.675 s ± 0.483 s [User: 17.264 s, System: 2.603 s]
Range (min … max): 9.048 s … 10.555 s 10 runs
Astro
❯ hyperfine 'npm run build'
Benchmark 1: npm run build
Time (mean ± σ): 22.342 s ± 0.535 s [User: 7.807 s, System: 1.424 s]
Range (min … max): 21.574 s … 23.284 s 10 runs
生成されたページのパフォーマンス
Astroは読み込むファイル数が圧倒的に少なく、Largest Contentful Paint (LCP) も短い。Next.jsはやはりHydrationの分、Astroよりも不利になる。
Next.js
Astro
どちらを使うか?
生成されるページのパフォーマンスを比べるとAstroがかなり魅力的に映る。コンテンツ主体のWebサイトであればNext.jsを採用する必要性はあまりないし、Astroを試したいところ。
懸念はビルド時間だが、AstroでSSGする場合の個人的ベストプラクティス - console.lealog(); にあるように、先にデータを一括でAPI取得しておいて、ビルド時にはそのデータを使うようにすることで、現実的な時間に軽減できそう。
事前に取得したデータを使うようにしてみた
Astroのビルドめちゃくちゃ早くなった
Next.js
❯ hyperfine 'npm run build'
Benchmark 1: npm run build
Time (mean ± σ): 8.884 s ± 1.386 s [User: 17.152 s, System: 2.717 s]
Range (min … max): 7.423 s … 11.599 s 10 runs
Astro
❯ hyperfine 'npm run build'
Benchmark 1: npm run build
Time (mean ± σ): 3.672 s ± 0.227 s [User: 4.295 s, System: 0.830 s]
Range (min … max): 3.177 s … 3.918 s 10 runs
記事にまとめた