🔖

Astroチュートリアルメモ その5-2,3【動的ルーティングとタグ一覧ページ】

2022/11/08に公開

タグのページを作る際に、動的ルーティングを使う。

https://docs.astro.build/en/tutorial/5-astro-api/2/

src/pages/tags/[tag].astroのようにファイルを作り、取りうるURLをgetStaticPathsで指定する。

Next.jsのgetStaticPathsとURLの作り方は同じ。

大きな違いはAstroのgetStaticPathsはNext.jsのgetStaticPathsgetStaticPropsを合わせた機能を持っているところ。

動的ページの作成

src/pages/tags/[tag].astro
---
import BaseLayout from "../../layouts/BaseLayout.astro";
import BlogPost from "../../components/BlogPost.astro";
export async function getStaticPaths({}) {
  const allPosts = await Astro.glob("../posts/*.md");
  return [
    { params: { tag: "astro" }, props: { posts: allPosts } },
    { params: { tag: "successes" }, props: { posts: allPosts } },
    { params: { tag: "community" }, props: { posts: allPosts } },
    { params: { tag: "blogging" }, props: { posts: allPosts } },
    { params: { tag: "setbacks" }, props: { posts: allPosts } },
    { params: { tag: "learning in public" }, props: { posts: allPosts } },
  ];
}

const { tag } = Astro.params;
const { posts } = Astro.props;
const filteredPosts = posts.filter((post) =>
  post.frontmatter.tags.includes(tag)
);
---

AstroのgetStaticPathsの作り方は基本はNext.jsのものと同じ。async awaitを使う。

params: { tag: "astro" }のオブジェクトとして、動的ルーティング用のIDとURLになりうる文字列の一覧を渡す。

さらに、getStaticPathsのreturnの中で、{ params: { tag: "astro" }, props: { posts: allPosts } },のように、記事データをpropsとして渡すことができる。

ページ内でpropsを使う

ページ内でparamsとpropsを使うためにオブジェクトを定義する。

paramstagとして、propspostsとして読み込む。

さらに、filterで動的ルーティングで指定されたタグを含むマークダウンファイルの記事データだけをfilteredPostsに読み込むことで、該当タグの一覧ページを作る。

src/pages/tags/[tag].astro
---
//抜粋

const { tag } = Astro.params;
const { posts } = Astro.props;
const filteredPosts = posts.filter((post) =>
  post.frontmatter.tags.includes(tag)
);
---

読み込んだtagfilteredPostsを表示部分の記述に書くことで、タグの一覧が完成する。

src/pages/tags/[tag].astro
<BaseLayout pageTitle={tag}>
  <p>Posts tagged with {tag}</p>
  <ul>
    {
      filteredPosts.map((post) => (
        <BlogPost url={post.url} title={post.frontmatter.title} />
      ))
    }
  </ul>
</BaseLayout>

Next.jsでタグ一覧を作ろうとすると、getStaticPropsで該当のデータを持ってきてそれをフィルターして、getStaticPropsのreturnで一覧をだして…となり、その一連の流れは全て自分で書くことになるが、Astroの場合は一部Astroの機能をそのまま使うことができる。

Astro.globでマークダウンファイル一覧を作ることができ、getStaticPathsで取りうるURLの一覧だけでなく、記事データも渡すことができる。上のチュートリアルの例では、その記事データをgetStaticPathsの外側で整形している。

Astroはマークダウンファイルを使ってブログを作る場合、カテゴリやタグを簡単に実装できるように最初から機能が作られているのが、Next.jsとのおおきな違い。ただ、別にNext.jsでも自分で書けば全部できるし、自分の好きなようにカスタマイズできる。

静的なページをさっと作るのにAstroは向いているっぽいのはこういう部分が作りこまれているからかなと思う。

タグを自動で取得してparamsを作る

上ではgetStaticPathsにタグと読み込む記事データを全てべた書きしているが、タグのデータはマークダウンファイル内に自由に書き込めるし、記事データも存在している。

すでに必要な全部のデータが、マークダウンの中に存在しているので、データから自動でparamsを作るほうが直感的というか、わかりやすいというか。自分がNext.jsでタグ一覧を作ったときもそんな風に作った記憶がある。

というわけで、チュートリアル通りにgetStaticPathsを書き換える。

上ではgetStaticPathsの外でアクセスのあったタグ名に対応するデータを整形していたが、その処理をgetStaticPaths内で先に自動で行う。

src/pages/tags/[tag].astro
export async function getStaticPaths({}) {
  const allPosts = await Astro.glob("../posts/*.md");
  const uniqueTags = [
    ...new Set(allPosts.map((post) => post.frontmatter.tags).flat()),
  ];
  return uniqueTags.map((tag) => {
    const filteredPosts = allPosts.filter((post) =>
      post.frontmatter.tags.includes(tag)
    );
    return {
      params: { tag },
      props: { posts: filteredPosts },
    };
  });
}

uniqueTagsでは、Astro.globで読み込んだallPostsのタグの配列を作り、それをflatで1次元にして、Setオブジェクトで重複を無くしている。最終的にSetオブジェクトをスプレッド構文で配列にしている。

uniqueTags
[
  'astro',
  'blogging',
  'learning in public',
  'successes',
  'setbacks',
  'community'
]

returnも書き換える。

先ほどは全てのタグに全ての記事データを送っていたが、各タグに対応する記事データだけをpropsに設定する。

タグに該当する記事データだけをfilteredPostsに入れる形。

以前Next.jsでブログを作ったときに、自身でタグ機能を実装したが、基本はこれと同じ作り方をしたので、非常になじみ深いというか、理解しやすかった。

ブログの動的ルーティングの場合、基本的にurlに紐づいて必要な記事データも決まるわけだから、getStaticPaths内で完結させればいいよね的な思想は、なんとなく理解できる。

ブログ以外のサイトを作る場合はどういう形になるのか、どんな使い方を試したくなるのかは、そのケースにならないとわからなそう。Astroの機能だけで完結できるのか、別のことをする必要があるかは今後使ってみての課題。

タグ一覧ページを作る

動的ルーティングで作った各タグページへの導線(タグ一覧)を作る。

https://docs.astro.build/en/tutorial/5-astro-api/3/

Next.jsと同じで、動的ルーティングのフォルダの中にindex.astroを作ることでタグのトップを作ることができる。

├─tags
│ ├─[tag].astro
│ └─index.astro

上の場合、[tag].astroドメイン/tags/タグ名ページに、index.astroドメイン/tags/になる。

スタイルシートは割愛。クラスをつけているのはチュートリアルの例ではcssを当てているため。

src/pages/tags/index.astro
--- 
import BaseLayout from '../../layouts/BaseLayout.astro';
const allPosts = await Astro.glob('../posts/*.md');
const tags = [...new Set(allPosts.map((post) => post.frontmatter.tags).flat())];
const pageTitle = "Tag Index";
---
<BaseLayout pageTitle={pageTitle}>
  <div class="tags">
    {tags.map((tag) => (
      <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
    ))}
  </div>
</BaseLayout>

何度も出てきているAstro.globで全てのマークダウンファイルのデータを取得し、記事データから重複無しのタグ一覧を作り、mapでタグ名の一覧を表示する。

この辺りもNext.jsで自分で作ったタグ一覧やカテゴリ一覧と同じ作り方なのでわかりやすかった。

次の記事

https://zenn.dev/k_neko3/articles/45481e6e4ddfe6

Discussion