高度なルーティング

404 ページの処理

ユーザーがサイトに存在しない URL にアクセスすることがあります。たとえば、ユーザーが https://example.com/does-not-exist にアクセスした場合、サーバーは404 HTTPステータスコードで応答する必要があり、ページは空白ページではなく、少なくとも何らかの説明が必要です。

特定のルートに対して、Qwik City は、デフォルトの 404 ページ、カスタム 404 ページ、または動的に生成された 404 ページのいずれを使用するかなど、ユーザーに対する 404 応答を最適に処理する方法を選択できます。

デフォルトの 404 ページ

空白ページを表示するのではなく、Qwik City はデフォルトで、処理されないルートに対して一般的な 404 ページを提供します。カスタム 404 ページが見つからなかった場合、デフォルトの 404 ページがフォールバックとしてレンダリングされます。より良いユーザーエクスペリエンスのために、カスタム 404 ページを提供することをお勧めします。カスタム 404 ページに共通のヘッダーとナビゲーションを含めると、ユーザーが探しているページを見つけるのに役立ちます。

カスタム 404 ページ

一般的な (退屈な) 404 応答を表示する代わりに、サイトの残りの部分と同じおなじみのレイアウトを使用してカスタム 404 ページを提供することができます。

カスタム 404 ページを作成するには、ルートの src/routes ディレクトリに 404.tsx ファイルを追加します。

src/
└── routes/
    ├── 404.tsx            # Custom 404
    ├── layout.tsx         # Default layout
    └── index.tsx          # https://example.com/

上記の例では、404.tsx ページは、同じディレクトリ内のレイアウトの兄弟であるため、layout.tsx レイアウトも使用します。

さらに、Qwik City のディレクトリベースのルーティングを使用すると、異なるパスにカスタム 404 ページを作成できます。たとえば、src/routes/account/404.tsx も構造に追加された場合、カスタムアカウント 404 ページは /account/* ルートにのみ適用され、他のすべての 404 はルートの 404.tsx ページを使用します。

注: 開発中およびプレビューモードでは、カスタム 404 ページはレンダリングされませんが、代わりにデフォルトの Qwik City 404 ページが表示されます。ただし、本番環境用にアプリをビルドするとき、カスタム 404 ページは静的な 404.html ファイルとして静的に生成されます。

src/
└── routes/
    ├── account/
       └── 404.tsx        # Custom Account 404
       └── index.tsx      # https://example.com/account/
    ├── 404.tsx            # Custom 404
    ├── layout.tsx         # Default layout
    └── index.tsx          # https://example.com/

カスタム 404 ページはビルド時に静的に生成され、個別にサーバーサイドでレンダリングされるページではなく、静的な 404.html ファイルになることに注意してください。この戦略により、HTTP サーバーの負荷が軽減され、404 ページのサーバーサイドレンダリングが回避され、リソースが保持されます。

動的な 404 ページ

ページをレンダリングする場合、デフォルトでは常に 200 HTTP ステータスコードで応答します。これは、ブラウザにすべてが正常でルートが存在することを伝えます。ただし、ページをレンダリングする処理をしますが、応答ステータスコードを手動で 200 以外のもの、たとえば 404 に設定することもできます。

たとえば、https://example.com/product/abc のような URL を持つ製品ページがあるとします。製品ページは src/routes/product/[id]/index.tsx ディレクトリベースのルートを使用して処理され、[id] は URL 内の動的パラメーターです。

この例では、id はデータベースから製品データをロードするためのキーとして使用されます。製品データが見つかった場合、素晴らしい、データを正しくレンダリングします。ただし、製品データがデータベースに見つからない場合は、ページをレンダリングする処理を継続できますが、代わりに 404 HTTP ステータスコードで応答します。

import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductLoader = routeLoader$(async ({ params, status }) => {
  // Example database call using the id param
  // The database could return null if the product is not found
  const data = await productDatabase.get(params.id);
 
  if (!data) {
    // Product data was not found
    // Set the status code to 404
    status(404);
  }
 
  // return the data (which may be null)
  return data;
});
 
export default component$(() => {
  // get the product data from the loader
  const product = useProductLoader();
 
  if (!product.value) {
    // no product data found
    // so render our own custom product 404
    return <p>Sorry, looks like we don't have this product.</p>;
  }
 
  // product data was found, so let's render it
  return (
    <div>
      <h1>{product.value.name}</h1>
      <p>{product.value.price}</p>
      <p>{product.value.description}</p>
    </div>
  );
});

グループ化されたレイアウト

共通の目的を持つルートは、レイアウトを共有できるようにディレクトリに配置されることが多く、関連するソースファイルが互いに論理的にグループ化されます。ただし、同様のファイルをグループ化してレイアウトを共有するために使用されたディレクトリが、一般公開される URL から除外されることが望ましい場合があります。これが「グループ化された」レイアウト(「パスレス」レイアウトルートとも呼ばれます)の出番です。

(name) のように、ディレクトリ名を括弧で囲むと、ディレクトリ名自体は URL パス名に含まれません。

たとえば、アプリがすべてのアカウントルートをディレクトリにまとめて配置したとします。 /account/ は、よりクリーンで短い URL のために URL から削除できます。以下の例では、パスが src/routes/(account)/ ディレクトリ内にあることに注意してください。ただし、URL パスには (account)/ は含まれていません。

src/
└── routes/
    └── (account)/             # Notice the parentheses
        ├── layout.tsx         # Shared account layout
        └── profile/
            └── index.tsx      # https://example.com/profile
        └── settings/
            └── index.tsx      # https://example.com/settings

名前付きレイアウト

関連するルートは、兄弟ルートとは大幅に異なるレイアウトが必要な場合があります。単一のデフォルトレイアウトと任意の数の名前付きレイアウトを使用することにより、異なる兄弟ルートに対して複数のレイアウトを定義できます。子ルートは、特定の名前付きレイアウトを要求できます。

Qwik City は、レイアウトが src/routes 内にあり、ファイル名が layout で始まるという規則を定義しています。そのため、デフォルトのレイアウトは layout.tsx という名前になっています。名前付きレイアウトも layout で始まり、その後にダッシュ - と一意の名前が続きます(例:layout-narrow.tsx)。

名前付きレイアウトを参照するには、ルートの index.tsx ファイルに @<name> を付加する必要があります。たとえば、[email protected]layout-narrow.tsx レイアウトを使用します。

src/
└── routes/
    ├── contact/
       └── [email protected]      # https://example.com/contact (Layout: layout-narrow.tsx)
    ├── layout.tsx                # Default layout
    ├── layout-narrow.tsx         # Named layout
    └── index.tsx                 # https://example.com/ (Layout: layout.tsx)
  • https://example.com/
    ┌──────────────────────────────────────────────────┐
    │       src/routes/layout.tsx                      │
    │  ┌────────────────────────────────────────────┐  │
    │  │    src/routes/index.tsx                    │  │
    │  │                                            │  │
    │  └────────────────────────────────────────────┘  │
    │                                                  │
    └──────────────────────────────────────────────────┘
  • https://example.com/contact
    ┌──────────────────────────────────────────────────┐
    │       src/routes/layout-narrow.tsx               │
    │  ┌────────────────────────────────────────────┐  │
    │  │    src/routes/contact/[email protected]     │  │
    │  │                                            │  │
    │  └────────────────────────────────────────────┘  │
    │                                                  │
    └──────────────────────────────────────────────────┘

ネストされたレイアウト

ほとんどの場合、レイアウトを互いにネストすることが望ましいです。ページのコンテンツは、ディレクトリ構造によって決定される、多数のラップレイアウトの中にネストできます。

src/
└── routes/
    ├── layout.tsx           # Parent layout
    └── about/
        ├── layout.tsx       # Child layout
        └── index.tsx        # https://example.com/about

上記の例では、/aboutページコンポーネントの周りに適用される2つのレイアウトがあります。

  1. src/routes/layout.tsx
  2. src/routes/about/layout.tsx

この場合、レイアウトはページ上で互いにネストされます。

┌────────────────────────────────────────────────┐
│       src/routes/layout.tsx                    │
│  ┌──────────────────────────────────────────┐  │
│  │    src/routes/about/layout.tsx           │  │
│  │  ┌────────────────────────────────────┐  │  │
│  │  │ src/routes/about/index.tsx         │  │  │
│  │  │                                    │  │  │
│  │  └────────────────────────────────────┘  │  │
│  │                                          │  │
│  └──────────────────────────────────────────┘  │
│                                                │
└────────────────────────────────────────────────┘
src/routes/layout.tsx
import { component$, Slot } from '@builder.io/qwik';
 
export default component$(() => {
  return (
    <main>
      <Slot /> {/* <== Child layout/route inserted here */}
    </main>
  );
});
src/routes/about/layout.tsx
import { component$, Slot } from '@builder.io/qwik';
 
export default component$(() => {
  return (
    <section>
      <Slot /> {/* <== Child layout/route inserted here */}
    </section>
  );
});
src/routes/about/index.tsx
import { component$ } from '@builder.io/qwik';
 
export default component$(() => {
  return <h1>About</h1>;
});

上記の例では、次のHTMLがレンダリングされます。

<main>
  <section>
    <h1>About</h1>
  </section>
</main>

plugin@<name>.ts を使用したプラグイン

plugin.ts および plugin@<name>.ts ファイルは、ルートレイアウトが実行される前に入ってくるリクエストを処理するために、src/routes ディレクトリのルートに作成できます。

複数の plugin.ts ファイルを、それぞれ異なる名前で持つことができます。例えば、[email protected][email protected]@<name> はオプションであり、開発者がプラグインを識別するのに役立つ場合にのみ使用されます。

onRequestonGetonPost などのリクエストハンドラーは、server$ 関数が実行される前に呼び出されます。

plugin.ts ファイルの実行順序

plugin.ts ファイルが存在し、リクエストハンドラーがエクスポートされている場合、それらが最初に実行されます。

次に、plugin@<name>.ts ファイルからエクスポートされたリクエストハンドラーが、ファイル名のアルファベット順に実行されます。例えば、[email protected]onGet は、[email protected]onGet より前に実行されます。なぜなら、auth がアルファベット順で security より前にあるからです。

最後に、server$ 関数が存在する場合は、それが最後に実行されます。

貢献者

このドキュメントの改善にご協力いただいたすべての貢献者に感謝します!

  • manucorporat
  • adamdbradley
  • cunzaizhuyi
  • the-r3aper7
  • mhevery
  • jakovljevic-mladen
  • vfshera
  • thejackshelton
  • wtlin1228
  • hamatoyogi
  • jemsco
  • patrickjs