Astroを使用してニュースレターウェブサイトを作成します。

作成者:カランカラン
💡

質問やフィードバックがありましたら、フォームからお願いします

本文は台湾華語で、ChatGPT で翻訳している記事なので、不確かな部分や間違いがあるかもしれません。ご了承ください

私のニュースレターのウェブサイトを作成する際、Routingや同構性は私が最も重視する点ではなく、データベースと頻繁にやり取りしたり、ブラウザ側での高度なインタラクションが必要なわけでもなかったため、Next.jsのようなSSRフレームワークは少し過剰ではないかと考えました。確かにNext.jsは全静的出力もサポートしていますが、Reactコンポーネントを書く際の精神的負担が気になってしまいました。

初めてAstroを聞いたのは同僚の紹介でした。せっかくの機会だから試してみることにしました。AstroはSFC(シングルファイルコンポーネント)を中心にした開発スタイルを提供しており、ウェブページを素早く簡単に構築できます。また、クラウドサービスに対応した構造も持っており、VercelやNetlify、GitHub Pagesなどのサービス向けに最適化されています。コードをアップロードするだけで、すぐに使用可能なウェブページを作成できます。

astro-deploy

市場にはすでに多くの静的サイトジェネレーターがありますが、なぜ新たに別のものが必要なのか疑問に思う方もいるでしょう。私のブログはJekyllやHexoから進化し、Gatsbyに至るまで、フレームワークの移行を何度も試みました。この経験を通じて、コンテンツ指向のウェブサイトを作成する際に重視すべきポイントがより明確になりました。

JekyllとHexoには多くの共通点があります。どちらもコンテンツサイトを構築するための基本機能を備えていますが、Markdownの拡張や高度なインタラクションを実現するMDXには相対的に難しさがあり、固有のテンプレート構文に適応しなければなりません。

Gatsbyを使用する際は、自分でGraphQLを書く必要があります。最初はこの柔軟性が楽しいのですが、スキーマを定義していない状態でMarkdownのfrontmatter属性を追加したいときには、非常に面倒になります。

そのうち、私はこうした状況に頻繁に陥りました。単にページを簡単に作成したいだけなのに、膨大な時間をGraphQLやReactコンポーネントの作成に費やさなければなりませんでした。

もう一つの選択肢は11tyで、Astroと非常に似ていますが、最大の違いはテンプレート構文にあると考えています。11tyはさまざまなテンプレート構文をサポートしていますが、何年もReactで書いてきた私にとっては、JSXが最も直感的な方法のように感じられ、Astroはまさにそれを実現しています。以下に、Astroが他のジェネレーターと異なる点をいくつか共有します。

シングルファイルコンポーネント(Single-File Components)

Astroの設計では、各ファイルが完全なコンポーネントとして扱われます。

つまり、JavaScript(ビルド時にのみ実行)、HTML、スタイルを同じファイルに書くことができ、フレームワークがクラス名の隔離を処理します。JSXやVueを書けるなら、Astroのコンポーネントも書けるので、別のテンプレート構文を学ぶ必要はありません。

---
const variable = 'Hello';
---

<section>
  <p>
    {variableA}
  </p>
</section>

<style>
  p { font-size: 14px; }
</style>

ウェブページを書くときにJavaScript、HTML、CSSを同じファイルに書けるのは非常に快適で、Astroはデフォルトでこれをサポートしており、追加の設定は不要です。

コンポーネントの構築方法はVueやSvelteと似ていますが、注意すべきはAstroがデフォルトでSSRではなく、フロントエンドフレームワークではないことです。ページはすべてビルド時に実行され、追加のJavaScriptは生成されません。(SSRモードを有効にしていない限り)

ブラウザ側でJavaScriptを実行してインタラクションを行うにはどうすれば良いでしょうか?Astroは二つの方法を提供しています:

  • <script>内に書くが、DOMバインディング機能がないため、document.querySelector()のようなコードを書く必要があります。
  • 他のフロントエンドフレームワークを直接インポートする

Astro Island

時には、大部分のページが静的でありながら、一部のページやその一部で大規模なインタラクションが必要なケースがあります。例えば、ブログのコメントセクションなどです。

Astroでは他のフレームワークのコンポーネントファイルをインポートでき、ReactやVueなど、対応するアダプターがあれば追加できます。この機能は私にとって特にユニークで、静的サイトジェネレーターにさらなる柔軟性をもたらします。

<aside>
  <ReactOrVueComponent client:load />
</aside>

このコードでは、Astroがコンパイル後に非Astroコンポーネントに遭遇した場合、空のDOMを作成し、ブラウザ側でJavaScriptを読み込んでからコンポーネントのDOMノードをマウントします。ここでのclient:loadはスクリプトがどのタイミングで読み込まれるべきかを示しています。このオプションの一つは、client:mediaclient:visibleがあり、特定のデバイスでのみ読み込みたいコンポーネント(例えば、スマートフォン画面でのみスワイプ可能なメニューなど)や、特定の位置に到達したときのみコンポーネントを読み込む場合に役立ちます。フレームワークの助けがなければ、これらを解決するために自分でJavaScriptを書く必要がありますが、Astroはそれをすべて解決してくれます。

ここで一つの問題があります。異なるフレームワーク(例えばAstroとReact)間で状態を共有するには、Astroはすでに解決策を提供していますが、新たにライブラリを導入することで、コストが利益を上回るのではないかという疑問も生じます。

私は静的サイトジェネレーターに他のフロントエンドフレームワークのサポートを追加することに対して肯定的な見解を持っています。Astro自体が大部分のウェブサイトのニーズを満たしているかもしれませんが、もし本当に純粋なインタラクションのウェブサイトを作成するつもりなら、最初からAstroを選ぶことはないでしょう。そしてフロントエンドフレームワークのサポートを追加することで、少数のウェブサイトのニーズを満たしつつ、ウェブサイトを迅速に構築できる必要がある場合、Preactを導入してインタラクションを書くことも非常に簡単です。

コンテンツコレクション(Content Collections)

AstroにはContent Collectionsという機能があります。簡単に言うと、静的なMarkdownファイルをクエリするのを助けてくれます。content/フォルダー内のファイルはすべてContent Collectionsとして扱われます。

import { getEntry } from "astro:content";
const entry = await getEntry("weekly", "my-first-weekly.md");

const { Content } = await entry.render();

AstroはMarkdownをページとして直接レンダリングできますが、より多くのページ調整の柔軟性を保持したい場合は、内蔵の検索関数を使用して検索することもできます。entry.render()は、処理済みの<Content />コンポーネントを返し、好きな位置に配置することでMarkdownの内容をHTML形式でレンダリングできます。

型サポート

AstroはTypeScriptをサポートしており、静的ページであってもPropsを宣言することで、そのコンポーネントが受け取れる属性を定義できます。これにより、使いやすさが大幅に向上します。

// MyComponent.astro
export interface Prop {
  user: string
}

// <MyComponent user="kalan" />

コンポーネントの属性だけでなく、コンテンツも定義できます。例えば、文章の定義で一般的にfrontmatterを使用して、タイトル、概要、画像などのメタデータを定義します。

---
title: これはタイトルです
summary: これは概要です
image: https://image.com
---

Astroはスキーマ定義の機能を提供しており(背後の実装はzod)、content/config.tsにスキーマの型を定義するだけで、getCollectiongetEntryを使用する際に対応する型を得ることができます。

import { z, defineCollection } from "astro:content";

const weeklyCollection = defineCollection({
  type: "content",
  schema: z.object({
    published_at: z.date(),
    issue_num: z.number(),
    title: z.string(),
    description: z.string().optional(),
    image: z.string().optional(),
  }),
  /* ... */
});

export const collections = {
  weekly: weeklyCollection,
};

// 他のAstroコンポーネント内で呼び出す

import { getCollection, getEntry } from "astro:content";

const entry = await getEntry("weekly", 'my-first-weekly.md'); // entryは上記のweeklyCollectionからweekly型を解析します

Astroの使用感

全体的に、Astroは非常に快適な印象を与えてくれます。特別な機能が不要な場合は、変数を埋め込むことができるHTMLとして書くのも全く問題ありません。

静的サイトジェネレーターの機能は似たり寄ったりですが、上記で述べた機能は他のツールでも実現可能です。では、Astroが優れている点はどこにあるのでしょうか?私は快適な開発者体験、シンプルで柔軟な構文(jsx)、特定のシーンにおけるデザインの適切さ(他のフロントエンドフレームワークのサポートや多様なスクリプトの読み込み方法)、およびクラウドサービスとの統合だと考えています。

静的サイトを構築するフロントエンドの方法は、ここ数年で大きく変化しました。Gatsbyが登場して以来、開発者はウェブページをより効率的に構築する方法を模索してきました。それは単にコンポーネントをサーバー側でレンダリングするだけではなく、JavaScriptの読み込みタイミング、Prefetch、Service Workerを通じたキャッシュ、画像最適化メカニズム、スタイル管理、エッジコンピューティング、開発速度の両立など、現代の静的サイトジェネレーターが考慮すべき機能です。もうMarkdownをHTMLに変換してサーバーにアップロードするだけの時代ではありません。

この記事が役に立ったと思ったら、下のリンクからコーヒーを奢ってくれると嬉しいです ☕ 私の普通の一日が輝かしいものになります ✨

Buy me a coffee