本文へ

開発記録

記事が増えたときに、ネイティブな静的ページネーションを採用する理由

当サイトの1ページ6件の通常記事一覧をもとに、公開条件による絞り込み、並び順、静的ルート、HTMLのページリンクが同じデータを使う仕組みと、ページ番号では保証できないことを整理します。

林予晴
公開 更新
  • コンテンツ設計
  • Astro
  • ページネーション
  • ネイティブHTML

記事一覧にカードが6枚だけ表示されていても、本当にページネーションがあるとは限りません。すべての記事をブラウザーに渡してから、残りをスクリプトで隠している場合もあれば、次のページに独立したURLとHTMLを用意している場合もあります。

当サイトでは後者を選んでいます。2ページ目は直接開ける静的ページであり、ページ番号のリンクはそのページを指します。これは、ローカルのMarkdownから公開ノートをビルドする現在の構成に合っています。ページを移動するために、ブラウザー側で別の状態管理を設ける必要はありません。

一覧に含める記事を決めてから、ページ数を計算します

現在の通常一覧は、1ページあたり最大6件です。NOTES_PER_PAGE に設定している値は 6 であり、現在の表示設定です。すべてのサイトが採用すべき基準ではありません。

ページに分割する前に、データにはいくつかの処理を行います。記事のソースから、対象言語で公開条件を満たす記事を選び、公開日時で並べ、同じ日時の場合は stableId で順序を決めます。その後、注目記事を除外し、通常一覧のページ数を計算して、対象ページの記事を取り出します。以下は、繁体字中国語の原稿の図をそのまま残したものです。

文章來源
  → 選出該語言已符合發布條件的文章
  → 依發布時間排序,同時間以 stableId 排定順序
  → 排除精選文章
  → 計算一般列表頁數,取出該頁項目

この順序は自由に入れ替えられません。先に6件ずつ分けてから下書きを除外すると、1ページに数枚のカードしか残らないことがあります。また、3言語の記事を混ぜてから分割すると、各言語の一覧が独自のページ数を持てなくなります。

注目記事は、有効な各ページの独立したブロックに表示され、通常一覧の6件分の枠は使いません。ここでは既存の注目記事のルールを引き継いでいます。ページネーションの役割は、その集合を一貫して使うことであり、別のおすすめ順を作ることではありません。

ページ番号には、実際に開けるルートがあります

繁体字中国語のルート例では、1ページ目に /notes を使い、それ以降は /notes/page/2、/notes/page/3 などを使います。英語と日本語には、それぞれの言語プレフィックスがあり、通常記事の総ページ数も言語ごとに計算します。

Astroの静的モードでは、動的ルートの getStaticPaths() で、生成するURLを列挙します。フレームワークには paginate() という補助関数もあります。[2] 当サイトでは、この補助関数を直接使っていません。サイト独自の paginateNotes() で一覧とsitemapのページ数計算を共通化し、その結果から2ページ目以降のルートパラメーターを返しています。

この違いは明確にしておく必要があります。paginateNotes() は当サイトのローカルなコードであり、別のAstro APIではありません。ネイティブな静的ページネーションとは、フレームワークの静的ルートとHTMLリンクを使うことです。すべての細部で組み込みの補助関数を使う、という意味ではありません。

ルートを生成するときは、実際に存在する2ページ目以降のページだけを列挙します。paginateNotes() は、内部で渡されたページ番号が範囲外なら有効な範囲内に収めます。ただし、これはデータ関数の保護処理です。任意のページ番号を入力したときのURLまで、サイトが生成しているわけではありません。

ページ移動のクリックを横取りする必要はありません

ページネーションのコンポーネントが出力するのは、<a href="…"> です。前のページと次のページへのリンクは、移動先が存在するときだけ表示します。現在のページ番号には aria-current="page" を付けています。

Astroの通常のルート移動には、標準のHTMLリンクを使います。[2] この一覧ではページ番号の移動先がすでに決まっているため、メモリー内の状態だけを更新するボタンに変える必要はありません。読者は2ページ目のURLをコピーでき、そのページを直接リクエストすることもできます。カードの内容は返されるHTMLにすでに含まれており、ページ移動のスクリプトが動くまで表示を待つ必要はありません。

これは、一覧とページ移動の役割についての説明です。サイトのほかの箇所では、アニメーション、ドロワー、部分的なスクリプトを使っている可能性があります。ページネーションにJavaScriptが不要だからといって、サイト全体にJavaScriptがないと説明することはできません。

ルート、一覧、sitemapでページ数をそろえます

ページの分割には通常記事だけを使い、sitemapでは注目記事も数えてしまうと、生成していない最終ページのURLを載せる可能性があります。逆に、ルートは生成したのにsitemapから漏らした場合も、二つの公開URL一覧が一致しません。

現在の一覧、2ページ目以降のルート、sitemapはいずれも対象言語の公開済み記事を基にしています。注目記事は有効な各一覧ページに表示されますが、通常一覧の枠やページ数には含めません。三つの出力で通常記事のページ分割基準を共有しています。

多言語の一覧では、すべての言語でページ数をそろえる必要もありません。繁体字中国語の原稿では、英語が2ページで、繁体字中国語には5ページ目がある場合を例にしています。このページ数は原稿が記録した時点の文脈であり、翻訳の追加後も同じページ数だという意味ではありません。その場合、繁体字中国語の5ページ目に合わせるためだけに、空の英語の5ページ目を作るべきではありません。

同じページ番号でも、内容が同じとは限りません。繁体字中国語の2ページ目と英語の2ページ目は、それぞれの言語の一覧における位置であり、記事の翻訳を対応付けた結果ではありません。同じ記事の別の言語版に切り替えるときは、引き続き記事ページの stableId の関係を使います。

ページは共有できますが、記事の位置は固定されません

新しい記事を公開すると並び順が変わり、1ページ目の末尾にあった記事が2ページ目へ移ることがあります。注目記事への指定を外したり、新しく指定したりすることで、通常一覧のページ数が変わる場合もあります。

そのため、ページ番号を含むURLは「この一覧のあるページ」を示すのに向いていますが、記事自体の恒久的な入口の代わりにはなりません。特定の記事を共有するなら、記事のルートを使います。3ページ目を、その記事がいつまでも置かれる場所として扱うべきではありません。

静的ページネーションには更新のコストもあります。ソース、公開条件を満たすかどうか、並び順が変わったら、読者に新しい一覧を届けるためにサイトを再ビルドし、更新する必要があります。リアルタイム検索でも、カーソルベースのページネーションでもありません。継続的に変化する個人の取引データを、そのまま扱う用途にも向いていません。

今回整理しているのは公開記事であり、頻繁に更新される業務データではないため、私はこのページネーションを維持します。1ページ6件が読みやすいかどうかは、タイトルの長さやスマートフォンでの読み方を見て判断する必要があります。独立したHTMLがあるだけで、読み込み速度、検索順位、読了率が改善したと結論付けることもできません。

この記録の範囲

この記事で説明したのは、既存の通常一覧、ページネーションのルート、ページネーションのコンポーネント、sitemapの役割分担です。検索、無限スクロール、リアルタイムのクエリーを追加したわけではなく、ページネーション導入前後のパフォーマンス比較も示していません。

参考記事:注目記事と日時順の並び替えを両立する仕組み。

参考資料

[2] https://docs.astro.build/en/guides/routing — Astro — Routing