🚀

噂のAstroに入門したので爆速でブログを作ってみる

2024/05/24に公開

dotD フロントエンドエンジニアの松村です。

弊社では普段、React や Next.js を使って開発することが多いのですが、
最近噂の Astro にやっと入門したので、紹介します。

Astroとは

Astro

Astro は、静的サイトジェネレータとして動作するフレームワークです。
以下のような特徴があります。

  • アイランドアーキテクチャ:UI を独立したコンポーネントに分割できる Web アーキテクチャを採用してる。
  • 自由な UI:React、Vue、Svelte、Solid などで作成されたコンポーネントをサポートしている。
  • ゼロ JS:静的サイトとして生成することで、クライアントサイドの JavaScript を減らし、ページのスピードを向上させる。
  • SSR も選択可能:静的サイトジェネレータとして構築されたが、サーバサイドレンダリングを併用できる。ページ毎にどちらを使用するか選択できる。

Astroできること・できないこと

LP やブログ、オウンドメディア、EC サイト、ポートフォリオなど、ユーザーにコンテンツを届けるいわゆる「Web サイト」を、高速で SEO に優れた形で構築できます。

一方で、ログインが必要なアプリケーションや、複雑な UI を持つ Web アプリケーションの構築には向いていません。

Astroをインストール

セットアップウィザードを開始するには下記のコマンドを叩きます。

# create a new project with yarn
yarn create astro

ウィザードの案内に従って設定を選択していきます。

プロジェクトを作成する場所を選択します。
今回は、コマンドを実行したディレクトリの配下にmy-blogという名前で作ります。

 astro   Launch sequence initiated.

   dir   Where should we create your new project?
         ./my-blog

プロジェクトに最初からテンプレートを含めるかどうかを選択します。
空でも良いですが、今回はブログテンプレートで作ります。

  tmpl   How would you like to start your new project?
         ○ Include sample files 
         ● Use blog template 
         ○ Empty 

TypeScript で書くかどうかを選択します。

    ts   Do you plan to write TypeScript?
         ● Yes  ○ No 

TypeScript の Strict モードにするかどうかを選択します。

   use   How strict should TypeScript be?
         ● Strict (recommended)
         ○ Strictest 
         ○ Relaxed 

依存関係をインストールするかどうかです。
特に理由がなければ Yes を選択します。

  deps   Install dependencies? (recommended)
         ● Yes  ○ No 

Git リポジトリの設定をするかどうかを選択します。

   git   Initialize a new git repository? (optional)
         ● Yes  ○ No 

以上を選択すると、セットアップが始まります。

      ✔  Project initialized!
         ■ Template copied
         ■ TypeScript customized
         ■ Dependencies installed
         ■ Git initialized

  next   Liftoff confirmed. Explore your project!

         Enter your project directory using cd ./my-brog 
         Run yarn dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯
✨  Done in 333.26s.

完了するとこんなファイル構成になります。
Next.js を使ったことがある方は、親近感のある構成だと思います。

テンプレートのディレクトリ構成

たったこれだけでブログサイトの雛形が完成です。

記事コンテンツの管理

ブログの記事はcontentディレクトリに格納していくのですが、
Markdown ファイルで配置可能です。
(もちろん、HTML もサポートしています)

記事コンテンツファイル

ですので、Web 技術に明るくない、運用担当者やライターでも簡単に記事を追加できます。

ページのルーティング

ページのルーティングは Next.js の pages router のように、
pagesディレクトリのファイル単位で処理されます。

pages/index.astroがルートコンテンツとして機能します。
pages/about.astro/aboutのパスからアクセスできます。

また、動的なルーティングもサポートされています。
例えばpages/blog/[...slug].astroというファイルを作成すると、
記事のスラッグに合わせて/blog/first-postのようなパスでアクセスできます。

デザインの反映

全ページ共通のスタイルは、styles/global.cssに記述します。

ページやコンポーネント固有のスタイルは、各ファイルに<style>タグの中に記述します。
小難しいことを考えなくても、生の HTML/CSS にかなり近い形で記述できます。

components/Header.astro
---
import HeaderLink from './HeaderLink.astro';
import { SITE_TITLE } from '../consts';
---

<header>
  <nav>
    <h2><a href="/">{SITE_TITLE}</a></h2>
    <div class="internal-links">
      <HeaderLink href="/">Home</HeaderLink>
      <HeaderLink href="/blog">Blog</HeaderLink>
      <HeaderLink href="/about">About</HeaderLink>
    </div>
  </nav>
</header>
<style>
  header {
    margin: 0;
    padding: 0 1em;
    background: white;
    box-shadow: 0 2px 8px rgba(var(--black), 5%);
  }
  h2 {
    margin: 0;
    font-size: 1em;
  }

  h2 a,
  h2 a.active {
    text-decoration: none;
  }
  nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  nav a {
    padding: 1em 0.5em;
    color: var(--black);
    border-bottom: 4px solid transparent;
    text-decoration: none;
  }
  nav a.active {
    text-decoration: none;
    border-bottom-color: var(--accent);
  }
</style>

レイアウト

レイアウトは、ページテンプレートのような再利用可能な UI 構造を作成するための Astro コンポーネントで、
ヘッダ、フッタ、ナビゲーションなどページ間で共通の UI を追加できます。
また、<slot />によって、個々のページのコンテンツが挿入される位置を指定します。

Markdown で書かれた記事コンテンツに対しても、レイアウトを使って共通 UI を挿入したり、スタイルを当てることができます。

以下はレイアウトファイルのサンプルです。

layouts/BlogPost.astro
---
import type { CollectionEntry } from 'astro:content';
import BaseHead from '../components/BaseHead.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import FormattedDate from '../components/FormattedDate.astro';

type Props = CollectionEntry<'blog'>['data'];

const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
---

<html lang="en">
  <head>
    <BaseHead title={title} description={description} />
    <style>
      main {
        width: calc(100% - 2em);
        max-width: 100%;
        margin: 0;
      }
      .hero-image {
        width: 100%;
      }
      .title {
        margin-bottom: 1em;
        padding: 1em 0;
        text-align: center;
        line-height: 1;
      }
    </style>
  </head>

  <body>
    <Header />
    <main>
      <article>
        <div class="hero-image">
          {heroImage && <img width={1020} height={510} src={heroImage} alt="" />}
        </div>
        <div class="prose">
          <div class="title">
            <div class="date">
              <FormattedDate date={pubDate} />
              {
                updatedDate && (
                  <div class="last-updated-on">
                    Last updated on <FormattedDate date={updatedDate} />
                  </div>
                )
              }
            </div>
            <h1>{title}</h1>
            <hr />
          </div>
          <slot /> <!-- ここにコンテンツが挿れられます -->
        </div>
      </article>
    </main>
    <Footer />
  </body>
</html>

レイアウトファイルを使う側のサンプルです。

pages/blog/[...slug].astro
---
import { type CollectionEntry, getCollection } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: post,
  }));
}
type Props = CollectionEntry<'blog'>;

const post = Astro.props;
const { Content } = await post.render();
---

<BlogPost {...post.data}>
  <Content />
</BlogPost>

getStaticPaths関数でcontent/blog配下の記事コンテンツを取得しています。
ヘッドレス CMS 等の API からコンテンツを取得することも可能です。

Build

yarn buildコマンドでビルドします。
ビルドが完了すると、distディレクトリが作成され、その中に静的ファイルが生成されます。

ビルドで生成されたのファイル群

よく見ると sitemap も自動で生成されていて、SEO を意識していることがわかります。

コンテンツを公開するには、このdistディレクトリ配下の全ファイルを、コンテンツ配信サーバにアップロードします。
SSR(サーバーサイドレンダリング)を使っていなければすべて静的ファイルですので、S3 などのストレージサービスに配置しても OK です。

感想

普段は React や Next.js を使うことが多いですが、それらと比べると
生の HTML/CSS 記述に近く、導入のハードルはかなり低いです。

Astro に React や Vue のコンポーネントを持ち込めるので、
React や Vue の初学者が入門編として触ってみるのも良いんじゃないでしょうか。

今回は、contentディレクトリに直接ファイルを作成しましたが、
Headless CMS 等の API からコンテンツを取得することも可能です。
なので実務レベルでも、今後 EC サイトやオウンドメディアを立ち上げる機会があれば、積極的に使っていきたいと思います。

dotDTechBlog

Discussion