• #nuxt
  • #breadcrumb
  • #directory-listing
  • #implementation
  • #troubleshooting
開発blog-platform完了

パンくずメニューとディレクトリ一覧機能の実装

概要

Nuxt Content + MDX サイトに、パンくずメニューとディレクトリ一覧表示機能を追加しました。

実装した機能

1. パンくずメニュー (Breadcrumb.vue)

場所: apps/web/app/components/Breadcrumb.vue

機能:

  • 現在のURLパスに基づいてパンくずリストを自動生成
  • 最後の項目(現在のページ)はリンクなしで表示
  • 中間パスはすべてリンク付きで表示

実装のポイント:

// 最後の項目のみリンクなし
<NuxtLink v-if="index < breadcrumbs.length - 1" :to="item.path">
  {{ item.label }}
</NuxtLink>
<span v-else aria-current="page">{{ item.label }}</span>

2. ディレクトリ一覧表示

場所: apps/web/app/pages/[...slug].vue

機能:

  • ディレクトリパスにアクセスした際に、配下の記事一覧を表示
  • 記事が存在しない場合は "Not Found" を表示

重要な問題と解決策:

問題: queryCollection で取得した記事の _pathnull

現象:

const articles = await queryCollection("pages").all();
// articles[0]._path === null (すべて null)

原因:

  • Nuxt Content の記事オブジェクトは、_path プロパティではなく path プロパティを持っている

解決策:

// ❌ 間違い
page._path?.startsWith(docPath.value + "/")

// ✅ 正しい
const pagePath = page.path || page._path;
pagePath.startsWith(docPath.value + "/")

最終的な実装:

const { data: allPages } = await useAsyncData(
  'all-pages-for-dir',
  () => queryCollection("pages").all()
);

const articles = computed(() => {
  if (!allPages.value) return [];
  return allPages.value.filter(page => {
    const pagePath = page.path || page._path;
    if (!pagePath) return false;
    return pagePath.startsWith(docPath.value + "/") && pagePath !== docPath.value;
  });
});

3. 記事一覧テーブルコンポーネント化

場所: apps/web/app/components/ArticleTable.vue

機能:

  • タイトル、作成日、タグを表形式で表示
  • ページネーション機能付き
  • 20件以下の場合はページネーションを非表示

使用箇所:

  • インデックスページ (apps/web/app/pages/index.vue)
  • ディレクトリ一覧ページ (apps/web/app/pages/[...slug].vue)

実装のポイント:

<!-- ページネーションは記事が20件を超える場合のみ表示 -->
<div v-if="totalPages > 1" class="pagination">
  <!-- ... -->
</div>

トラブルシューティング

問題1: 開発サーバーのキャッシュ問題

現象:

  • ファイルを修正しても古いエラーが表示され続ける
  • [vue/compiler-sfc] Unexpected token エラーが消えない

原因:

  • Nuxt の開発サーバーが古いコンパイル結果をキャッシュしている

解決方法:

  1. 開発サーバーを停止 (Ctrl+C)
  2. .nuxt フォルダを削除(オプション)
  3. pnpm run dev で再起動

問題2: 正規表現での置換ミス

現象:

  • replace_regex で意図しない部分まで削除されてしまった
  • スクリプトセクションに余分な閉じ括弧が残った

教訓:

  • 複雑な正規表現は慎重に使用する
  • 置換後は必ずファイル全体を確認する
  • 重要な変更の前にはバックアップを取る

ファイル構成

apps/web/app/
├── components/
│   ├── Breadcrumb.vue          # パンくずメニュー
│   ├── ArticleTable.vue         # 記事一覧テーブル
│   ├── DocPage.vue              # 記事表示ページ
│   └── TableOfContents.vue      # 目次
├── pages/
│   ├── index.vue                # トップページ
│   └── [...slug].vue            # 動的ルート(記事・ディレクトリ一覧)

使用方法

パンくずメニュー

パンくずメニューは自動的に表示されます。URLパスに基づいて生成されます。

例:

  • /2025-10-06/sns-principles の場合
    • Home / 2025-10-06 / sns-principles
    • Home2025-10-06 はリンク付き
    • sns-principles はリンクなし(現在のページ)

ディレクトリ一覧

ディレクトリパス(例: /2025-10-06)にアクセスすると、配下の記事が自動的に一覧表示されます。

表示形式:

  • タイトル(記事へのリンク)
  • 作成日
  • タグ

今後の改善案

  1. パンくずメニューのカスタマイズ
    • セグメントのラベルをカスタマイズできるようにする
    • アイコンの追加
  2. ディレクトリ一覧の拡張
    • ソート順の変更機能
    • フィルター機能
    • 説明文の表示
  3. パフォーマンス最適化
    • 記事一覧のキャッシング
    • 遅延読み込み

参考