カランのブログ

ソフトウェアエンジニア / 台湾人 / 福岡生活

今のモード ライト

電子報のウェブサイトを作成している際に、ルーティングや同構造化は私の最も関心があるポイントではないことに気づきました。また、頻繁にデータベースからデータを取得したり、ブラウザ側で対話を行ったりする必要もありません。そのため、Next.jsのようなSSRフレームワークは強力すぎるのではないかと考えました。Next.jsは完全静的な出力もサポートしていますが、Reactコンポーネントの作成に関する認識負荷がまだ私を躊躇させています。

Astroについては、同僚から紹介を受けたのが初めてでした。この機会を逃すわけにはいかないと思いました。AstroはSFC(シングルファイルコンポーネント)を中心とした開発方法を提供しており、ウェブページを簡単かつ迅速に構築することができます。また、クラウドのトレンドに合わせて、Vercel、Netlify、Github Pagesなどのサービスに対応しており、コードをアップロードするだけで利用可能なウェブページを作成できます。

市場には既に多くの静的サイトジェネレータが存在しているかもしれませんが、なぜ別のものが必要なのでしょうか?私のブログはJekyllからHexo、そしてGatsbyへと進化してきました。フレームワークを何度も変更した経験から、自分がコンテンツ中心のウェブサイトを作成する際に重視するポイントがより明確になりました。

JekyllとHexoは多くの共通点を持っています。これらはコンテンツサイトを構築するための基本的な機能を備えていますが、Markdownの拡張やより高度な対話性のあるMDXの実装は比較的困難であり、それら固有のテンプレート構文に適応する必要があります。

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

結果的に、私は次のような状況に頻繁に陥りました:単純なページを作成したいだけなのに、GraphQLとReactコンポーネントを作成するために多くの時間を費やさなければならないということです。

もう1つのオプションは11tyです。これはAstroと非常に似ていますが、私にとって最大の違いはテンプレート構文です。11tyはさまざまなテンプレート構文をサポートしていますが、私が数年間Reactで開発してきたため、JSXが最も直感的な方法に思えます。Astroはちょうどそれを実現しています。以下に、Astroと他のジェネレータとの明らかな違いと思われるいくつかのポイントを共有します。

シングルファイルコンポーネント

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には2つの方法があります:

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

Astro Island

時折、大部分のページが静的であるが、1つまたは2つのページやページの一部に大量の対話が必要な場合があります。Astroでは、ブラウザの対話に他のフレームワークのコンポーネントファイルを参照することができます。例えば、ReactやVueなどのフレームワークが対応している場合、それらを追加できます。この機能は、静的サイトジェネレータに柔軟性をもたらす特別な機能と言えるでしょう。

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

このコードでは、Astro以外のコンポーネントに遭遇した場合、Astroはコンパイル後に空のDOMを作成し、ブラウザでJavaScriptをロードしてからコンポーネントのDOMノードをマウントします。ここでのclient:loadは、スクリプトをいつロードするかを示しています。このオプションでは、client:mediaclient:visibleも使用できます。

特定のデバイスでのみ読み込みたいコンポーネント(例:モバイル画面でのスライド可能なメニュー)や、特定の位置にスクロールしたときにのみコンポーネントを読み込みたい場合があります。フレームワークのサポートがない場合、これらの問題を解決するためにJavaScriptを追加で記述する必要がありますが、Astroはそれをすべて処理してくれます。

ここで1つの問題があります。異なるフレームワーク(例:Astro、React)は、どのように状態を共有するのでしょうか。Astroは既に解決策を提供していますが、ライブラリを追加することで、コストと利益のバランスがどちらが大きいか疑問に思うかもしれません。

私は静的サイトジェネレータに他のフロントエンドフレームワークのサポートを追加することに肯定的な考えを持っています。Astro自体がほとんどのウェブサイトの要件を満たしているかもしれませんが、純粋な対話型のウェブサイトを作成したい場合は、最初からAstroを選択しないでしょう。フロントエンドフレームワークのサポートを追加することで、一部のウェブサイトの要件を満たすことができ、ウェブサイトを迅速に構築することができます。

コンテンツコレクション

Astroには「コンテンツコレクション」という機能があります。これは静的なMarkdownファイルをクエリするためのものです。content/ディレクトリ以下のファイルはすべてコンテンツコレクションとして扱われます。

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からの型を解析します

Astroの使用感

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

静的サイトジェネレータの機能は大差ないですが、上記で述べた機能は他のツールでも実現できる方法があります。では、Astroの利点は何でしょうか?私が感じるのは、快適な開発体験、シンプルかつ柔軟な構文(JSX)、特定のシナリオに対する設計の優れた点(他のフロントエンドフレームワークのサポートやスクリプトの多重読み込み方法)、そしてクラウドサービスとの統合です。

近年、フロントエンドにおける静的サイトの構築方法は大きく変化しました。Gatsbyを始めとして、開発者はウェブページをより効率的に構築する方法を探し続けており、サーバーサイドレンダリングだけでなく、JavaScriptの読み込みタイミング、プリフェッチ、Service Workerを使用したキャッシュ、画像の最適化、スタイル管理、エッジコンピューティング、開発速度の両立など、モダンな静的サイトジェネレータが考慮すべき機能が増えています。もはやMarkdownをHTMLに変換してサーバーにアップロードするだけではありません。

次の記事

社交恐怖症

前の記事

深層職場力 by Cal Newport

この文章が役に立つと思うなら、下のリンクで応援してくれると大変嬉しいです✨

Buy me a coffee

作者

Kalan 頭像照片,在淡水拍攝,淺藍背景

愷開 | Kalan

Kalan です。台湾出身で、2019年に日本へ転職し、福岡に住んでいます。フロントエンド開発に精通しているだけでなく、IoT、アプリ開発、バックエンド、電子工作などの分野にも挑戦しています。 最近、エレキギターを始めました。ブログを通じて、より多くの人と交流できればと思っています。気軽に絡んでください