AstroとNewtでの動的ルーティングとページネーション

komosyu
Astro

目次

  1. はじめに
  2. Astro での動的ルーティング
    1. 1.動的にパラメーターを指定し…
    2. 2.getStaticPaths 関数内で動的ルーティングの処理を行う
    3. 2-1.Newt から投稿データを取得してくる
    4. 2-2.Newt から取得してきたデータからファイル名に当てたい値を返す
    5. 2-3.[slug].astroに表示したいコンテンツを返す
  3. Astro でのページネーション
    1. 1.ページ番号を振りたいファイルを…
    2. 2.getStaticPaths 関数内で paginate 関数を使ってページを生成
    3. 2-1.Newt から投稿データを取得してくる
    4. 2-2.paginate()関数でページネーションの設定をする
    5. 2-3.前後送り以外のページネーションも
  4. 参考
  5. さいごに

はじめに

今回は SSG フレームワークを Astro、HeadlessCMS を Newt でブログサイトを実装してみた経験を記事にしてみることにしました。
どちらも比較的あたらしいモノで、調べてみても情報がそこまで多くなくて自分の中で苦戦することが多かったので、今後同じようなところでつまってしまった方のお役に立てれば嬉しいです!

内容としては、Astro での動的ルーティング、ページネーションあたりのお話が中心になるかと思います。

Astro での動的ルーティング

それでは、さっそく Astro での動的ルーティングの方法についてみてみましょう!
今回は例としてブログの記事ページみたいなイメージで pages ディレクトリ直下に用意した記事ページで、動的ルーティングを行う想定で解説していきます。

実装の流れとしては以下のような感じになるかと思います。

  • 1.動的にパラメーターを指定したいディレクトリやファイルを[slug].astroみたいな感じでブラケット記法を用いて用意しておく
  • 2.getStaticPaths 関数内で動的ルーティングの処理を行う
  • 2-1.Newt から投稿データを取得してくる
  • 2-2.Newt から取得してきたデータからファイル名に当てたい値を返す
  • 2-3.[slug].astroに表示したいコンテンツを返す

それでは、具体的にみていきましょう!

1.動的にパラメーターを指定し…

これは特に説明する必要もなく、そのまま/pages/[slug].astroという感じで pages ディレクトリ直下に動的ルーティングさせたいページをブラケット記法で用意しておくだけです。

もちろん、ファイル名は必ず[slug].astroにする必要はなくて自由にしていただいて問題ないです。

ただ、今回はわかりやすくするためにこのようにしています。

2.getStaticPaths 関数内で動的ルーティングの処理を行う

Next.js を使ったことがあれば、ある程度同じノリが通用するので直感的に理解できると思います。

具体的な実装方法は以下で解説していきます。

2-1.Newt から投稿データを取得してくる

今回は HeadlessCMS に Newt を使用しているので、Newt 側から [slug] に使いたい値を渡してもらう必要があります。

なので、まずは Newt からデータを取得してくるところからやっていきましょう。

より詳細な内容は以前のブログでも解説しているので、今回は getStaticPaths 関数内での処理だけにとどめます。

export async function getStaticPaths() {
  const postsData = await client.getContents({
    appUid: 'appuid',
    modelUid: 'modeluid',
  })
}

こんな感じで Newt からデータを取得してきます。

ただ、postData のままではこんな感じのデータ構造になっているので

{
  skip: 0,
  limit: 100,
  total: 5,
  items: [
    _id: '',
    _sys: [Object],
    // ここからはNewt管理画面で設定した内容
    title: '',
    slug: 'blog-post-1',
    thumbnail: [],
    date: '',
    content: ""
  ]
}

実際にコンテンツの表示に関わる内容である items の内容が使いやすいように 変数postsContents を用意しておきます。

export async function getStaticPaths() {
  const postsData = await client.getContents({
    appUid: 'appuid',
    modelUid: 'modeluid',
  })
  const postsContents = postsData.items
}

2-2.Newt から取得してきたデータからファイル名に当てたい値を返す

それでは、ここからが本番ですね。
先ほど取得してきたデータからファイル名に当てたい値を返していきましょう。

export async function getStaticPaths() {
  // Newtのデータを取得
  const postsData = await client.getContents({
    appUid: 'appuid',
    modelUid: 'modeluid',
  })
  const postsContents = postsData.items

  const pages = postsContents.map((postsContent) => {
    return {
      // [slug].astroに当てたい値を返す
      params: {
        slug: postsContent.slug,
      },
    }
  })
  return pages
}

上記のコードを見ると分かると思いますが、変数page の中で先ほど取得してきたpostsContentsを展開していって、ブログとして投稿した内容から[slug]に入る値が欲しいので、postsContent.slugの値を返します。

今は map で処理されていますが、1 つずつ中身をみてみるとこんな感じになっています。

return [
  // 生成対象: /blog-post-1
  { params: { slug: 'blog-post-1' } },
  // 生成対象: /blog-post-2
  { params: { slug: 'blog-post-2' } },
  // 生成対象: /blog-post-3
  { params: { slug: 'blog-post-3' } },
]

そして、最後にpagesを getStaticPaths 関数の中で返してあげれば動的ルーティングの完成です!

2-3.[slug].astroに表示したいコンテンツを返す

動的ルーティングで値を返すだけであれば、上記のままで問題ないのですが、そのままでは[slug].astroページに表示するコンテンツがないということで、その辺りも簡単に解説していきます!

---
export async function getStaticPaths() {
  const postsData = await client.getContents({
    appUid:  'appuid',
    modelUid: 'modeluid',
  })
  const postsContents = postsData.items

  const pages = postsContents.map((postsContent) => {
    return {
      params: {
        slug: postsContent.slug,
      },
      // 表示したいコンテンツはここで返す
      props: {
        postsContent,
      },
    }
  })
  return pages
}

// そして、ページ分割されたデータをpostsContentプロパティとして渡す
const { postsContent } = Astro.props

---
// こんな感じで使う
<h1>{postsContent.title}</h1>

ほとんど上記コード内にコメントとして残しましたが、表示したいコンテンツはparamsの方ではなくpropsの方に渡します。

そして、getStaticPaths 関数の外でAstro.propsオブジェクトで分割代入して利用します。

Astro でのページネーション

それでは、動的ルーティングが理解できたところでページネーションの方も見てみましょう!

Astro はコンテンツの豊富な Web サイトに特化しているということもあり、ページネーションもデフォルト用意されています。(さすが!)

しかし、ドキュメントを見る限りでは「前のページ」「次のページ」「現在のページ」程度しか作れないため、仕様によっては自分でこの辺りを調整して実装する必要がありそうです。

この辺りは後ほど解説していこうと思います。

まず、こちらも実装の流れから見てみましょう。

  • 1.ページ番号を振りたいファイルを[...page].astroみたいな感じでブラケット記法を用いて用意しておく
  • 2.getStaticPaths 関数内で paginate 関数を使ってページを生成
  • 2-1.Newt から投稿データを取得してくる
  • 2-2.paginate 関数でページネーションの設定をする
  • 2-3.前後送り以外のページネーションも

それでは、具体的にみていきましょう!

1.ページ番号を振りたいファイルを…

例えば、トップページにページネーションをつけたい場合には、/pages/[...page].astroといった感じでファイルを作成しておきます。

[...page].astroではなく[page].astroでもいいのですが、レストパラメータを使用することで深さに関係なく、すべてのファイルパスを受け取ることができます。

具体的にどういうことかというと、例えばページネーションを作って 1 ページ目 2 ページ目 3 ページ目の表示を作られると、[page].astroの場合は/1,/2,/3と問題なく作っていくことができますが、トップページの url/ は作成されません。

もし、(というかほとんどの場合トップページは)/1じゃなくて/にしたいという場合はレストパラメーターを使って[...page].astroとしておいた方がいいかなと思います。

2.getStaticPaths 関数内で paginate 関数を使ってページを生成

getStaticPaths を使うのは前章で解説した動的ルーティングと同様なのですが、ページネーションを作成するには paginateという Astro で用意されている関数を使います。

具体的な方法は以下で解説していきます。

2-1.Newt から投稿データを取得してくる

まず動的ルーティングで解説した時と同様に、こちらでも Newt からデータを取得してくるところからはじめていきます。

export async function getStaticPaths() {
  const postsData = await client.getContents({
    appUid: 'appuid',
    modelUid: 'modeluid',
  })
  const postsContents = postsData.items
}

同じです。

2-2.paginate()関数でページネーションの設定をする

ここからようやくページネーションの実装に入りますが、非常にシンプルです。

export async function getStaticPaths({ paginate }) {
  const postsData = await client.getContents({
    appUid: 'appuid',
    modelUid: 'modeluid',
  })
  const postsContents = postsData.items

  return paginate(postsContents, { pageSize: 10 })
}

getStaticPaths 関数の中でpaginate関数を返しています。

中身を見てみると、表示したいコンテンツを第 1 引数に、ページ内に表示したいコンテンツの数を第 2 引数に設定しておきます。

簡単ですね!

そのあと、どうやって使っていくかというとこんな感じです。

---
const { page } = Astro.props
---
// 公式より

// 現在のページ
<h1>{page.currentPage}ページ</h1>
// コンテンツの表示
<ul>
  {page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>
// ページネーション
{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}
{page.url.next ? <a href={page.url.next}>Next</a> : null}

getStaticPaths 関数の外でAstro.propsオブジェクトで分割代入して利用します。

2-3.前後送り以外のページネーションも

最後におまけで、公式で用意されている前後送り以外のページネーションじゃ実務で使えないだろ!という方もいると思うので、個人的に作ったページネーションのロジックがあるので、よかったら使ってみてください。

ここでは Astro と Newt の話がメインなので、細かくページネーションのロジックについては触れませんが、使い方としてはPaginationコンポーネントに先ほど作成したpageを渡してあげると、すべての数字が展開されて邪魔にならないように...が出て省略されるようにしております。

参考

さいごに

今回は Astro と Newt での動的ルーティングとページネーションについて解説してみました。

やはり、Web サイトに特化しているフレームワークというだけあって Astro は割と直感的に実装することができますね。

はじめて触ってみた時も Next.js を経験していたらか、同じようなノリで実装することができたのですんなり進めることができました。

そんな感じで、比較的とっかかりやすいフレームワークだと思うので、気になった方はぜひ使ってみてください。