概要
前回の記事広告連携(Google AdSense)で広告連携を扱いました。今回の記事では静的サイトでサーバーなしに検索機能を実装する方法を説明します。
Pagefindとは
Pagefindは静的サイトのための検索ライブラリです。ビルドされたHTMLファイルを分析して検索インデックスを生成し、クライアントサイドで検索を実行します。
主な特徴
他の検索ソリューション(Algolia、Elasticsearchなど)と異なり、別途サーバーなしに静的サイトだけで動作するのがPagefindの最大の利点です。
- サーバー不要:ビルド時にインデックスを生成して静的ファイルとして提供
- 軽量:検索インデックスが小さくJavaScriptバンドルも軽い
- 高速検索:WebAssemblyベースでブラウザで高速に動作
- 多言語対応:言語別インデキシングが可能
インストール
Pagefindはビルド時にのみ使用するため、開発依存パッケージ(-D)としてインストールします。
npm install -D pagefind
ビルド統合
package.jsonのbuildスクリプトでAstroビルド後にPagefindインデキシングを実行します。
{
"scripts": {
"build": "astro build && npx pagefind --site dist"
}
}
Pagefindはdist/ディレクトリのHTMLファイルを分析してdist/pagefind/ディレクトリに検索インデックスを生成します。そのため必ずastro buildの後に実行する必要があります。
検索対象の指定
Pagefindはデフォルトでページのすべてのテキストをインデキシングします。しかしナビゲーション、フッターなど本文以外の領域まで検索されると不必要な結果が表示される可能性があります。data-pagefind-body属性を使用すると検索対象を本文領域に限定できます。
<!-- src/layouts/PostLayout.astro -->
<article data-pagefind-body>
<slot />
<!-- マークダウンコンテンツ -->
</article>
検索から除外する要素にはdata-pagefind-ignore属性を使用します。
メタデータフィルター
検索結果をカテゴリ別にフィルタリングするにはdata-pagefind-filter属性でメタデータを指定します。この属性が含まれた要素のテキストがフィルター値として使用されます。
<!-- src/layouts/PostLayout.astro -->
<span data-pagefind-filter="category">{category}</span>
検索ページの実装
検索機能はSearchBar.astro(ホームページ検索バー)とSearchPage.astro(専用検索ページ)で実装されています。
SearchPage.astro主要構造
検索ページはカテゴリフィルターチップ、検索ステータス表示、検索結果リスト、ページネーションで構成されています。サーバーサイドでカテゴリ一覧と翻訳データを準備し、実際の検索はクライアントサイドJavaScriptで処理します。
---
// src/components/SearchPage.astro
import { useTranslations, getLocalizedPath } from '../i18n/translations';
import { getMainCategories } from '../data/categories';
const { lang } = Astro.props;
const t = useTranslations(lang);
const mainCategories = getMainCategories();
---
<div class="search-page">
<!-- カテゴリフィルターチップ -->
<div class="search-page__filters" id="search-filters">
<button class="search-page__chip active" data-category="">
{t('search.allCategories')}
</button>
{categoryChips.map((chip) => (
<button class="search-page__chip" data-category={chip.slug}>
{chip.title}
</button>
))}
</div>
<!-- 検索ステータス -->
<div class="search-page__status" id="search-status"></div>
<!-- 検索結果 -->
<div class="search-page__results" id="search-results"></div>
<!-- ページネーション -->
<nav id="search-pagination" class="search-page__pagination"></nav>
</div>
クライアントサイド検索ロジック
実際の検索はブラウザで実行されます。PagefindのJavaScript APIを使用して検索キーワードを入力するとビルド時に生成されたインデックスから結果を探して返します。
// src/components/SearchPage.astro 内クライアントスクリプト
const pagefind = await import('/pagefind/pagefind.js');
await pagefind.init();
// 検索実行
const search = await pagefind.search(query);
// 結果ロード
const results = await Promise.all(search.results.map((r) => r.data()));
URLクエリパラメータ(?q=検索キーワード)を使用して検索キーワードを維持するため、検索結果ページをブックマークしたり共有したりできます。
多言語検索
このブログは日本語、韓国語、英語をサポートしているため、各言語別に別々の検索ページを提供しています。
| 言語 | 検索ページURL |
|---|---|
| 日本語 | /search/ |
| 韓国語 | /ko/search/ |
| 英語 | /en/search/ |
Pagefindのインデックスにはすべての言語のコンテンツが含まれますが、検索時に該当言語の結果のみフィルタリングして表示します。
カテゴリフィルタリング
検索結果が多い時に見たいカテゴリだけを選べると便利です。categories.tsに定義されたカテゴリ一覧をチップボタンで表示し、ユーザーが選択したカテゴリで結果をフィルタリングします。
// src/components/SearchPage.astro 内クライアントスクリプト
const search = await pagefind.search(query, {
filters: {
category: selectedCategory,
},
});
Jekyll検索との比較
現在のブログはJekyllで運営していたものをAstroにマイグレーション中です。Jekyll検索実装については検索バー作成ポストを参考にしてください。
JekyllとAstro(Pagefind)の検索実装の主な違いは以下の通りです。
| 項目 | Jekyll | Astro(Pagefind) |
|---|---|---|
| 検索方式 | JSONファイルベースのクライアント検索 | WebAssemblyベースのインデックス検索 |
| インデックスサイズ | 全コンテンツをJSONで生成(サイズ大) | 最適化されたバイナリインデックス(小さい) |
| 検索速度 | JavaScript文字列マッチング | WebAssemblyで高速検索 |
| 設定の複雑さ | カスタムJSON生成 + 検索ロジックが必要 | npx pagefind一行でインデックス生成 |
| カテゴリフィルター | 自前実装が必要 | data-pagefind-filter属性で簡単に実装 |
完了
今回の記事ではPagefindを利用した静的検索の実装を見てきました。
Pagefindでサーバーなしにクライアントサイド検索を実装astro build && npx pagefind --site distでビルドパイプラインに統合data-pagefind-body、data-pagefind-filterで検索対象とフィルターを指定- 言語別検索ページで多言語検索をサポート
- カテゴリチップボタンで結果をフィルタリング
次の記事レイアウトとコンポーネントアーキテクチャではブログのレイアウトとコンポーネント構造を扱います。
シリーズ案内
このポストはJekyllからAstroへのマイグレーションシリーズの一部です。
- JekyllからAstroへマイグレーションした理由
- Astroのインストールとプロジェクト構成
- Content Collectionsとマークダウンマイグレーション
- 多言語(i18n)実装
- SEO実装
- 画像最適化 — カスタムrehypeプラグイン
- コメントシステム(Utterances)
- 広告連携(Google AdSense)
- Pagefindを利用した検索実装
- レイアウトとコンポーネントアーキテクチャ
- GitHub Pagesデプロイ
- ソーシャル共有自動化スクリプト
- トラブルシューティングとヒント
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。