diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..b5c68e5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/issue_template.md b/.github/ISSUE_TEMPLATE/issue_template.md index 4be775c..e04efde 100644 --- a/.github/ISSUE_TEMPLATE/issue_template.md +++ b/.github/ISSUE_TEMPLATE/issue_template.md @@ -4,7 +4,6 @@ about: Standard issue template for Nextjs-docs-ja title: '' labels: '' assignees: '' - --- #### Description diff --git a/docs/01-app/01-getting-started/02-layouts-and-pages.mdx b/docs/01-app/01-getting-started/02-layouts-and-pages.mdx new file mode 100644 index 0000000..15f3b96 --- /dev/null +++ b/docs/01-app/01-getting-started/02-layouts-and-pages.mdx @@ -0,0 +1,303 @@ +--- +title: 'レイアウトとページの作成方法' +nav_title: 'Layouts and Pages' +description: 'Next.jsアプリケーションでのレイアウトとページの作成方法、そしてそれらをリンクする方法を学びましょう。' +related: + links: + - app/api-reference/file-conventions/layout + - app/api-reference/file-conventions/page + - app/api-reference/components/link +--- + +Next.jsは**ファイルシステムベースのルーティング**を使用しており、フォルダとファイルを使ってルートを定義できます。このページでは、レイアウトとページの作成方法、そしてそれらをリンクする方法を説明します。 + +## ページの作成 {#creating-a-page} + +**ページ**は、特定のルートでレンダリングされるUIです。ページを作成するには、`app`ディレクトリの中に[`page`ファイル](/docs/app/api-reference/file-conventions/page)を追加し、Reactコンポーネントをデフォルトでエクスポートします。たとえば、インデックスページ(`/`)を作成するには次のようにします: + +page.jsの特別ファイル + + + + +```tsx title="app/page.tsx" switcher +export default function Page() { + return

Hello Next.js!

+} +``` + +
+ + +```jsx title="app/page.js" switcher +export default function Page() { + return

Hello Next.js!

+} +``` + +
+
+ +## レイアウトの作成 {#creating-a-layout} + +レイアウトは複数ページ間で**共有**されるUIです。ナビゲーション時にレイアウトは状態を保持し、インタラクティブな状態を維持し、再レンダリングされません。 + +レイアウトは[`layout`ファイル](/docs/app/api-reference/file-conventions/layout)からReactコンポーネントをデフォルトエクスポートすることで定義できます。このコンポーネントは`children`プロップを受け取り、ページまたは別の[layout](#nesting-layouts)になることができます。 + +たとえば、インデックスページを子として受け入れるレイアウトを作成するには、`app`ディレクトリに`layout`ファイルを追加します: + +layout.jsの特別ファイル + + + + +```tsx title="app/layout.tsx" switcher +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {/* レイアウト UI */} + {/* `children`をページまたはネストされたレイアウトをレンダリングしたい場所に配置 */} +
{children}
+ + + ) +} +``` + +
+ + +```jsx title="app/layout.js" switcher +export default function DashboardLayout({ children }) { + return ( + + + {/* レイアウト UI */} + {/* `children`をページまたはネストされたレイアウトをレンダリングしたい場所に配置 */} +
{children}
+ + + ) +} +``` + +
+
+ +上記のレイアウトは[ルートレイアウト](/docs/app/api-reference/file-conventions/layout#root-layouts)と呼ばれ、`app`ディレクトリのルートで定義されています。root レイアウトは**必須**であり、`html`と`body`タグを含んでいる必要があります。 + +## ネストされたルートの作成 {#creating-a-nested-route} + +ネストされたルートは、複数のURLセグメントで構成されるルートです。たとえば、`/blog/[slug]`ルートは次の3つのセグメントで構成されています: + +- `/` (Root セグメント) +- `blog` (セグメント) +- `[slug]` (Leaf セグメント) + +Next.jsでは: + +- **フォルダ**がルートセグメントを定義し、URLセグメントにマップされます。 +- **ファイル**(例えば`page`や`layout`)がセグメントに表示されるUIを作成します。 + +ネストされたルートを作成するには、フォルダを入れ子にします。たとえば、`/blog`ルートを追加するには、`app`ディレクトリに`blog`というフォルダを作成します。それから、`/blog`を公開されるようにするには`page`ファイルを追加します: + +blogフォルダとpage.jsファイルの階層を示す + + + + +```tsx title="app/blog/page.tsx" switcher +import { getPosts } from '@/lib/posts' +import { Post } from '@/ui/post' + +export default async function Page() { + const posts = await getPosts() + + return ( + + ) +} +``` + + + + + + +```jsx title="app/blog/[slug]/page.js" switcher +import { getPosts } from '@/lib/posts' +import { Post } from '@/ui/post' + +export default async function Page() { + const posts = await getPosts() + + return ( + + ) +} +``` + + + + +さらにフォルダを入れ子にしてネストされたルートを作成できます。たとえば、特定のブログ記事のルートを作成するには、`blog`の中に新しい`[slug]`フォルダを作成し、`page`ファイルを追加します: + +slugフォルダとpage.jsファイルがネストされたブログフォルダの階層を示す + + + + +```tsx title="app/blog/[slug]/page.tsx" switcher +function generateStaticParams() {} + +export default function Page() { + return

Hello, Blog Post Page!

+} +``` + +
+ + +```jsx title="app/blog/[slug]/page.js" switcher +function generateStaticParams() {} + +export default function Page() { + return

Hello, Blog Post Page!

+} +``` + +
+
+ +> **Good to know**: フォルダ名を角括弧で囲む(例: `[slug]`)と、特別な**dynamic route segment**が作成され、データから複数のページを生成します。これはブログ投稿や商品ページなどに役立ちます。[dynamic segments](/docs/app/building-your-application/routing/dynamic-routes)について詳しく学びましょう。 + +## レイアウトをネストする {#nesting-layouts} + +デフォルトでは、フォルダ階層においてレイアウトがネストされており、そのため`children`プロップを介して子レイアウトをラップします。特定のルートセグメント(フォルダ)に`layout`を追加することでレイアウトをネストできます。 + +たとえば、`/blog`ルートのレイアウトを作成するには、`blog`フォルダ内に新しい`layout`ファイルを追加します。 + +ルートレイアウトがブログレイアウトをラップするファイル階層を示す + + + + +```tsx title="app/blog/layout.tsx" switcher +export default function BlogLayout({ + children, +}: { + children: React.ReactNode +}) { + return
{children}
+} +``` + +
+ + +```jsx title="app/blog/layout.js" switcher +export default function BlogLayout({ children }) { + return
{children}
+} +``` + +
+
+ +もし上記2つのレイアウトを組み合わせると、root レイアウト(`app/layout.js`)がブログレイアウト(`app/blog/layout.js`)をラップし、ブログ(`app/blog/page.js`)とブログ投稿ページ(`app/blog/[slug]/page.js`)をラップします。 + +## ページ間をリンクする {#linking-between-pages} + +ルート間のナビゲーションには[``コンポーネント](/docs/app/api-reference/components/link)を使用できます。``はHTML ``タグを拡張してプリフェッチとクライアントサイドナビゲーションを提供する、Next.js組み込みのコンポーネントです。 + +たとえば、ブログ投稿のリストを生成するには、`next/link`から``をインポートし、`href`プロップをコンポーネントに渡します: + + + + +```tsx title="app/ui/post.tsx" highlight={1,6} switcher +import Link from 'next/link' + +export default async function Post({ post }) { + const posts = await getPosts() + + return ( +
    + {posts.map((post) => ( +
  • + {post.title} +
  • + ))} +
+ ) +} +``` + +
+ + +```jsx title="app/ui/post.js" highlight={1,6} switcher +import Link from 'next/link' + +export default async function Post({ post }) { + const posts = await getPosts() + + return ( +
    + {posts.map((post) => ( +
  • + {post.title} +
  • + ))} +
+ ) +} +``` + +
+
+ +``はNext.jsアプリケーションでルート間をナビゲートする際の主要で推奨される方法です。しかし、より高度なナビゲーションには[`useRouter`フック](/docs/app/api-reference/functions/use-router)も使用できます。 diff --git a/docs/01-app/01-getting-started/02-project-structure.mdx b/docs/01-app/01-getting-started/03-project-structure.mdx similarity index 100% rename from docs/01-app/01-getting-started/02-project-structure.mdx rename to docs/01-app/01-getting-started/03-project-structure.mdx diff --git a/docs/01-app/01-getting-started/04-images-and-fonts.mdx b/docs/01-app/01-getting-started/04-images-and-fonts.mdx new file mode 100644 index 0000000..e7a37c3 --- /dev/null +++ b/docs/01-app/01-getting-started/04-images-and-fonts.mdx @@ -0,0 +1,435 @@ +--- +title: '画像とフォントの最適化方法' +nav_title: '画像とフォント' +description: 'Next.jsで画像とフォントを最適化する方法を学ぶ' +related: + links: + - 'app/api-reference/components/font' + - 'app/api-reference/components/image' +--- + +Next.jsは、パフォーマンスとユーザーエクスペリエンスを向上させるために、画像とフォントの自動最適化機能を提供します。このページでは、これらを活用する方法についてご紹介します。 + +## 静的アセットの取り扱い {#handling-static-assets} + +画像やフォントなどの静的ファイルは、ルートディレクトリの`public`というフォルダに保存できます。`public`内のファイルは、ベースURL(`/`)から始めてコードで参照できます。 + +アプリフォルダーとパブリックフォルダーを示すフォルダー構造 + +## 画像の最適化 {#optmizing-images} + +Next.jsの[``](/docs/app/building-your-application/optimizing/images)コンポーネントは、HTMLの``要素を拡張して以下を提供します。 + +- **サイズの最適化:** WebPやAVIFなどのモダンな画像形式を使用して、各デバイスに対して適切なサイズの画像を自動的に提供 +- **視覚的な安定性:** 画像の読み込み時に、自動的に[レイアウトシフト](https://nextjs.org/learn/seo/web-performance/cls)を防止 +- **ページの読み込み速度の向上:** ネイティブブラウザの遅延読み込みを使用して、画像がビューポートに入るときにのみ読み込み、オプションのぼけアッププレースホルダーを使用可能 +- **アセットの柔軟性:** リモートサーバーに保存された画像を含め、オンデマンドで画像をリサイズ可能 + +``の使用を開始するには、`next/image`からインポートし、コンポーネント内でレンダリングします。 + + + + +```tsx title="app/page.tsx" switcher +import Image from 'next/image' + +export default function Page() { + return +} +``` + + + + +```jsx title="app/page.js" switcher +import Image from 'next/image' + +export default function Page() { + return +} +``` + + + + +`src`プロパティは、[ローカル](#local-images)または[リモート](#remote-images)の画像にすることができます。 + +### ローカル画像 {#local-images} + +ローカル画像を使用するには、[`public`フォルダ](#handling-static-assets)から`.jpg`、`.png`、または`.webp`画像ファイルを`import`します。 + + + + +```tsx title="app/page.ts" switcher +import Image from 'next/image' +import profilePic from './me.png' + +export default function Page() { + return ( + 著者の写真 + ) +} +``` + + + + +```jsx title="app/page.js" switcher +import Image from 'next/image' +import profilePic from './me.png' + +export default function Page() { + return ( + 著者の写真 + ) +} +``` + + + + +Next.jsは、インポートされたファイルに基づいて画像の固有の[`width`](/docs/app/api-reference/components/image#width)と[`height`](/docs/app/api-reference/components/image#height)を自動的に決定します。これらの値は画像比を決定し、画像の読み込み中に[Cumulative Layout Shift](https://nextjs.org/learn/seo/web-performance/cls)を防ぐために使用されます。 + +### リモート画像 {#remote-images} + +リモート画像を使用するには、`src`プロパティにURL文字列を指定します。 + + + + +```tsx title="app/page.ts" switcher +import Image from 'next/image' + +export default function Page() { + return ( + 著者の写真 + ) +} +``` + + + + +```jsx title="app/page.js" switcher +import Image from 'next/image' + +export default function Page() { + return ( + 著者の写真 + ) +} +``` + + + + +Next.jsはビルドプロセス中にリモートファイルにアクセスできないため、[`width`](/docs/app/api-reference/components/image#width)、[`height`](/docs/app/api-reference/components/image#height)、およびオプションの[`blurDataURL`](/docs/app/api-reference/components/image#blurdataurl)プロップを手動で指定する必要があります。これらの`width` と`height`属性は、画像の正しいアスペクト比を推定し、画像が読み込まれる際のレイアウトシフトを防ぐために使用されます。しかし、`width`と`height`は画像ファイルのレンダリングサイズを決定しません。 + +その後、リモートサーバーからの画像を安全に許可するために、`next.config.js`に対応するURLパターンのリストを定義する必要があります。不正使用を防ぐため、できるだけ具体的にしてください。たとえば、次の設定では、特定のAWS S3バケットからの画像のみを許可します。 + + + + +```ts title="next.config.js" switcher +import { NextConfig } from 'next' + +const config: NextConfig = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 's3.amazonaws.com', + port: '', + pathname: '/my-bucket/**', + search: '', + }, + ], + }, +} + +export default config +``` + + + + +```js title="next.config.js" switcher +module.exports = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 's3.amazonaws.com', + port: '', + pathname: '/my-bucket/**', + search: '', + }, + ], + }, +} +``` + + + + +[APIリファレンス](/docs/app/api-reference/components/image)で画像最適化について詳しく学んでください。 + +## フォントの最適化 {#optimizing-fonts} + +[`next/font`](/docs/app/api-reference/components/font)モジュールは、フォントを自動的に最適化し、外部ネットワークへのリクエストを削除してプライバシーとパフォーマンスを向上させます。 + +このモジュールには、*任意の*フォントファイルに対して**組み込みの自動セルフホスティング**が含まれています。このため、レイアウトシフトなしでWebフォントを最適にロードできます。 + +`next/font`の使用を開始するには、`next/font/local`または`next/font/google`([Googleフォント](#google-fonts)または[ローカルフォント](#local-fonts)の使用に応じて)からインポートし、適切なオプションで関数として呼び出し、適用したい要素の`className`を設定します。 + + + + +```tsx title="app/layout.tsx" switcher +import { Geist } from 'next/font/google' + +const geist = Geist({ + subsets: ['latin'], +}) + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import { Geist } from 'next/font/google' + +const geist = Geist({ + subsets: ['latin'], +}) + +export default function Layout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +### Googleフォント {#google-fonts} + +任意のGoogleフォントを自動的にセルフホスティングできます。フォントはデプロイメントに含まれ、デプロイメントと同じドメインから提供されます。**ブラウザからGoogleへのリクエストは送信されません。** + +Googleフォントを使用するには、`next/font/google`から関数としてインポートします。 + + + + +```tsx title="app/layout.tsx" switcher +import { Inter } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import { Inter } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +最適なパフォーマンスと柔軟性のために[可変フォント](https://fonts.google.com/variablefonts)の使用を推奨します。可変フォントを使用できない場合は、**ウェイトを指定する必要があります**。 + + + + +```tsx title="app/layout.tsx" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +### ローカルフォント {#local-fonts} + +ローカルフォントを使用するには、`next/font/local`をインポートし、[`public`フォルダ](#handling-static-assets)内のローカルフォントファイルの`src`を指定します。 + + + + +```tsx title="app/layout.tsx" switcher +import localFont from 'next/font/local' + +const myFont = localFont({ + src: './my-font.woff2', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import localFont from 'next/font/local' + +const myFont = localFont({ + src: './my-font.woff2', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +単一のフォントファミリーに対して複数のファイルを使用したい場合、`src`は配列にできます。 + +```js +const roboto = localFont({ + src: [ + { + path: './Roboto-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './Roboto-Italic.woff2', + weight: '400', + style: 'italic', + }, + { + path: './Roboto-Bold.woff2', + weight: '700', + style: 'normal', + }, + { + path: './Roboto-BoldItalic.woff2', + weight: '700', + style: 'italic', + }, + ], +}) +``` + +[APIリファレンス](/docs/app/api-reference/components/font)でフォント最適化について詳しく学んでください。 diff --git a/docs/01-app/01-getting-started/05-css-and-styling.mdx b/docs/01-app/01-getting-started/05-css-and-styling.mdx new file mode 100644 index 0000000..20df0e3 --- /dev/null +++ b/docs/01-app/01-getting-started/05-css-and-styling.mdx @@ -0,0 +1,650 @@ +--- +title: 'アプリケーションでCSSを使用する方法' +nav_title: 'CSSとスタイリング' +description: 'Next.jsアプリケーションにCSSとスタイリングを追加する方法を学びます。' +related: + links: + - 'app/api-reference/config/next-config-js/sassOptions' + - 'architecture/nextjs-compiler' +--- + +Next.jsはアプリケーションでCSSを使用するためのいくつかの方法を提供しています: + +- [CSS Modules](#css-modules) +- [グローバルCSS](#global-css) +- [Tailwind CSS](#tailwind-css) +- [Sass](#sass) +- [CSS-in-JS](#css-in-js) +- [外部スタイルシート](#external-stylesheets) + +このページでは、これらのアプローチをそれぞれ使用する方法を案内します。 + +## CSS Modules {#css-modules} + +CSS Modulesは、一意のクラス名を生成することでCSSをローカルにスコープします。これにより、異なるファイルで同じクラスを衝突を心配することなく使用することができます。 + +CSS Modulesの使用を開始するには、拡張子`.module.css`の新しいファイルを作成し、そのファイルを`app`ディレクトリ内の任意のコンポーネントにインポートします: + +```css title="app/blog/styles.module.css" +.blog { + padding: 24px; +} +``` + + + + +```tsx title="app/blog/page.tsx" switcher +import styles from './styles.module.css' + +export default function Page({ children }: { children: React.ReactNode }) { + return
{children}
+} +``` + +
+ + +```jsx title="app/blog/page.js" switcher +import styles from './styles.module.css' + +export default function Page({ children }) { + return
{children}
+} +``` + +
+
+ +> **Good to know:** +> +> - CSS Modulesは`.module.css`および`.module.sass`の拡張子があるファイルに対してのみ有効です。 +> - 本番環境では、すべてのCSS Moduleファイルは自動的に結合され、最小化され、コードスプリットされた`.css`ファイルになります。 +> - これらの`.css`ファイルは、アプリケーションの重要な実行パスに相当し、アプリケーションが描画するために必要な最小限のCSSがロードされることを保証します。 + +## グローバルCSS {#global-css} + +アプリケーション全体にスタイルを適用するためにグローバルCSSを使用することができます。 + +グローバルスタイルを使用するには、新しいCSSファイルを作成します(例:`app/global.css`): + +```css title="app/global.css" +body { + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} +``` + +ファイルをrootレイアウト(`app/layout.js`)にインポートして、アプリケーション内の**すべてのルート**にスタイルを適用します: + + + + +```tsx title="app/layout.tsx" switcher +// これらのスタイルはアプリケーション内のすべてのルートに適用されます +import './global.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +// これらのスタイルはアプリケーション内のすべてのルートに適用されます +import './global.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +> **Good to know:** グローバルスタイルは、`app`ディレクトリ内の任意のレイアウト、ページ、またはコンポーネントにインポートできます。ただし、Next.jsはReactの組み込みスタイルシートのサポートを使用してSuspenseと統合します。この組み込みサポートは、現在ルート間を移動するときにスタイルシートを削除しません。したがって、本当にグローバルなCSSの場合はグローバルスタイルを、スコープ付きCSSには[CSS Modules](#css-modules)を使用することをお勧めします。 + +## Tailwind CSS {#tailwind-css} + +[Tailwind CSS](https://tailwindcss.com/)は、Next.jsとシームレスに統合するユーティリティーファーストのCSSフレームワークです。 + +### Tailwindのインストール {#installing-tailwind} + +Tailwindを使用するには、Tailwind CSSのパッケージをインストールし、`init`コマンドを実行して`tailwind.config.js`と`postcss.config.js`ファイルを生成します: + +```bash title="Terminal" +npm install -D tailwindcss postcss autoprefixer +npx tailwindcss init -p +``` + +### Tailwindの設定 {#configuring-tailwind} + +Tailwindの設定ファイル内で、Tailwindのクラス名を使用するファイルのパスを追加します: + + + + +```ts title="tailwind.config.ts" switcher +import type { Config } from 'tailwindcss' + +const config: Config = { + content: [ + './app/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + // `src`ディレクトリを使用している場合: + './src/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: {}, + }, + plugins: [], +} +export default config +``` + + + + +```js title="tailwind.config.js" switcher +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './app/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + // `src`ディレクトリを使用している場合: + './src/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: {}, + }, + plugins: [], +} +``` + + + + +`postcss.config.js`を修正する必要はありません。 + +### Tailwindの使用 {#using-tailwind} + +[Tailwindディレクティブ](https://tailwindcss.com/docs/functions-and-directives#directives)を[グローバルスタイルシート](#global-css)に追加します。例えば: + +```css title="app/globals.css" +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +次に、スタイルをrootレイアウトにインポートします: + + + + +```tsx title="app/layout.tsx" switcher +import type { Metadata } from 'next' + +// これらのスタイルはアプリケーション内のすべてのルートに適用されます +import './globals.css' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +// これらのスタイルはアプリケーション内のすべてのルートに適用されます +import './globals.css' + +export const metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +最後に、アプリケーションでTailwindのユーティリティクラスを記述することができます。 + + + + +```tsx title="app/page.tsx" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +
+ + +```jsx title="app/page.js" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +
+
+ +## Sass {#sass} + +Next.jsは、[`.scss`](https://sass-lang.com/documentation/syntax/#scss)および[`.sass`](https://sass-lang.com/documentation/syntax#the-indented-syntax)拡張子と構文を使用して[Sass](https://sass-lang.com/)と統合します。 + +また、[CSS Modules](#css-modules)と`.module.scss`または`.module.sass`拡張子を用いたコンポーネントレベルのSassも使用できます。 + +### Sassのインストール {#installing-sass} + +Sassを使用するには、`sass`パッケージをインストールします: + +```bash title="Terminal" +npm install --save-dev sass +``` + +### Sassのオプションのカスタマイズ {#customizing-sass-options} + +Sassのオプションを設定したい場合は、`next.config.js`で`sassOptions`を使用します。 + + + + +```ts title="next.config.ts" switcher +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + sassOptions: { + additionalData: `$var: red;`, + }, +} + +export default nextConfig +``` + + + + +```js title="next.config.js" switcher +/** @type {import('next').NextConfig} */ + +const nextConfig = { + sassOptions: { + additionalData: `$var: red;`, + }, +} + +module.exports = nextConfig +``` + + + + +`sassOptions`の設定については、[APIリファレンス](/docs/app/api-reference/config/next-config-js/sassOptions)を詳しく学びましょう。 + +## CSS-in-JS {#css-in-js} + +> **警告:** ランタイムJavaScriptを必要とするCSS-in-JSライブラリは、現在ReactのServer Componentsではサポートされていません。Server ComponentsやStreamingのような最新のReact機能とCSS-in-JSを使用するには、ライブラリアーキテクトが最新のReactバージョンをサポートする必要があります。 + +次のライブラリは、`app`ディレクトリ内の**Client Components**でサポートされています(アルファベット順): + +- [`ant-design`](https://ant.design/docs/react/use-with-next#using-app-router) +- [`chakra-ui`](https://chakra-ui.com/getting-started/nextjs-app-guide) +- [`@fluentui/react-components`](https://react.fluentui.dev/?path=/docs/concepts-developer-server-side-rendering-next-js-appdir-setup--page) +- [`kuma-ui`](https://kuma-ui.com) +- [`@mui/material`](https://mui.com/material-ui/guides/next-js-app-router/) +- [`@mui/joy`](https://mui.com/joy-ui/integrations/next-js-app-router/) +- [`pandacss`](https://panda-css.com) +- [`styled-jsx`](#styled-jsx) +- [`styled-components`](#styled-components) +- [`stylex`](https://stylexjs.com) +- [`tamagui`](https://tamagui.dev/docs/guides/next-js#server-components) +- [`tss-react`](https://tss-react.dev/) +- [`vanilla-extract`](https://vanilla-extract.style) + +以下は現在サポートに取り組んでいます: + +- [`emotion`](https://github.com/emotion-js/emotion/issues/2928) + +Server Componentsをスタイル設定する場合は、[CSS Modules](#css-modules)や[Tailwind CSS](#tailwind-css)のようにCSSファイルを出力する他のソリューションを使用することをお勧めします。 + +### CSS-in-JSの設定 {#configuring-css-in-js} + +CSS-in-JSを設定するには、以下を行う必要があります: + +1. **スタイルレジストリ**を作成して、レンダー中にすべてのCSSルールを集約します。 +2. `useServerInsertedHTML`フックを使用して、ルールをそれを使用する可能性のあるすべてのコンテンツの前に挿入します。 +3. 最初のサーバーサイドレンダリング中にスタイルレジストリでアプリをラップするClient Componentを作成します。 + +#### `styled-jsx` {#styled-jsx} + +アプリケーション用に`styled-jsx`を設定するには、新しいレジストリを作成します: + + + + +```tsx title="app/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ + children, +}: { + children: React.ReactNode +}) { + // 遅延初期状態でスタイルシートを一度だけ作成します + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + + + + +```jsx title="app/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ children }) { + // 遅延初期状態でスタイルシートを一度だけ作成します + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + + + + +次に、レジストリで[root レイアウト](/docs/app/api-reference/file-conventions/layout#root-layouts)をラップします: + + + + +```tsx title="app/layout.tsx" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + + + + +#### `styled-components` {#styled-components} + +`styled-components`を使用するには、`next.config.js`で有効にします: + + + + +```ts title="next.config.ts" switcher +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + compiler: { + styledComponents: true, + }, +} + +export default nextConfig +``` + + + + +```js title="next.config.js" switcher +/** @type {import('next').NextConfig} */ +const nextConfig = { + compiler: { + styledComponents: true, + }, +} +``` + + + + +次に、`styled-components` APIを使用して、レンダー中に生成されたすべてのCSSスタイルルールを収集するグローバルレジストリコンポーネントを作成し、それらのルールを返す関数を作成します。その後、`useServerInsertedHTML`フックを使用して、root レイアウト内の`` HTMLタグにレジストリで収集したスタイルを注入します。 + + + + +```tsx title="lib/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ + children, +}: { + children: React.ReactNode +}) { + // 遅延初期状態でスタイルシートを一度だけ作成します + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + + + + +```jsx title="lib/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ children }) { + // 遅延初期状態でスタイルシートを一度だけ作成します + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + + + + +root レイアウトの`children`をスタイルレジストリコンポーネントでラップします: + + + + +```tsx title="app/layout.tsx" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + + + + +## 外部スタイルシート {#external-stylesheets} + +外部パッケージで公開されたスタイルシートは、`app`ディレクトリ内のどこにでもインポートできます。これは、コンポーネントに配置することもできます: + + + + +```tsx title="app/layout.tsx" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + + + + +```jsx title="app/layout.js" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + +外部スタイルシートは、npmパッケージから直接インポートするか、ダウンロードしてコードベースと共に配置する必要があります。``を使用することはできません。 diff --git a/docs/01-app/01-getting-started/06-data-fetching-and-streaming.mdx b/docs/01-app/01-getting-started/06-data-fetching-and-streaming.mdx new file mode 100644 index 0000000..6824b7c --- /dev/null +++ b/docs/01-app/01-getting-started/06-data-fetching-and-streaming.mdx @@ -0,0 +1,402 @@ +--- +title: 'データを取得してストリームする方法' +nav_title: 'データの取得とストリーム' +description: 'Next.jsアプリケーションでデータの取得とコンテンツのストリーム方法を学ぶ' +related: + links: + - 'app/api-reference/functions/fetch' + - 'app/api-reference/file-conventions/loading' +--- + +このページでは、[Server Components](#server-components)と[Client Components](#client-components)でデータを取得する方法、およびデータに依存するコンテンツを[ストリームする](#streaming)方法を説明します。 + +## データの取得 {#fetching-data} + +### Server Components {#server-components} + +Server Componentでデータを取得するには、次の方法があります: + +1. [`fetch` API](#with-the-fetch-api)を使用する +2. [ORMまたはデータベース](#with-an-orm-or-database)を使用する + +#### `fetch` APIを使用する {#with-the-fetch-api} + +`fetch` APIを使用してデータを取得するには、コンポーネントを非同期関数にし、`fetch`の呼び出しを待ちます。例: + + + + +```tsx title="app/blog/page.tsx" switcher +export default async function Page() { + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+ + +```jsx title="app/blog/page.js" switcher +export default async function Page() { + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+
+ +#### ORMまたはデータベースを使用する {#with-an-orm-or-database} + +コンポーネントを非同期関数にし、呼び出しを待つことで、ORMまたはデータベースを使用してデータを取得できます: + + + + +```tsx title="app/blog/page.tsx" switcher +import { db, posts } from '@/lib/db' + +export default async function Page() { + const allPosts = await db.select().from(posts) + return ( +
    + {allPosts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+ + +```jsx title="app/blog/page.js" switcher +import { db, posts } from '@/lib/db' + +export default async function Page() { + const allPosts = await db.select().from(posts) + return ( +
    + {allPosts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+
+ +### Client Components {#client-components} + +Reactの[`use`フック](https://react.dev/reference/react/use)を使用して、サーバーからクライアントにデータを[ストリームする](#streaming)ことができます。まず、Server Componentでデータを取得し、そのプロミスを`props`としてClient Componentに渡します: + + + + +```tsx title="app/blog/page.tsx" switcher +import Posts from '@/app/ui/posts' +import { Suspense } from 'react' + +export default function Page() { + // データ取得関数を待たないでください + const posts = getPosts() + + return ( + Loading...}> + + + ) +} +``` + + + + +```jsx title="app/blog/page.js" switcher +import Posts from '@/app/ui/posts' +import { Suspense } from 'react' + +export default function Page() { + // データ取得関数を待たないでください + const posts = getPosts() + + return ( + Loading...}> + + + ) +} +``` + + + + +次に、Client Componentで、`use`フックを使用してプロミスを読み取ります: + + + + +```tsx title="app/ui/posts.tsx" switcher +'use client' +import { use } from 'react' + +export default function Posts({ posts }) { + const posts = use(posts) + + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+ + +```jsx title="app/ui/posts.js" switcher +'use client' +import { use } from 'react' + +export default function Posts({ posts }) { + const posts = use(posts) + + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+
+ +上記の例では、コンポーネントを[``境界](https://react.dev/reference/react/Suspense)でラップする必要があります。これは、プロミスが解決される間にフォールバックが表示されることを意味します。詳しくは[ストリーミング](#streaming)について学んでください。 + +あるいは、[SWR](https://swr.vercel.app/)や[React Query](https://tanstack.com/query/latest)のようなコミュニティライブラリを使用することもできます。これらのライブラリは、キャッシュやストリーミング、その他の機能に関して独自のセマンティクスを持っています。例: + + + + +```tsx title="app/blog/page.tsx" switcher +'use client' +import useSWR from 'swr' + +const fetcher = (url) => fetch(url).then((r) => r.json()) + +export default function BlogPage() { + const { data, error, isLoading } = useSWR( + 'https://api.vercel.app/blog', + fetcher + ) + + if (isLoading) return
Loading...
+ if (error) return
Error: {error.message}
+ + return ( +
    + {data.map((post: { id: string; title: string }) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+ + +```jsx title="app/blog/page.js" switcher +'use client' +import useSWR from 'swr' + +const fetcher = (url) => fetch(url).then((r) => r.json()) + +export default function BlogPage() { + const { data, error, isLoading } = useSWR( + 'https://api.vercel.app/blog', + fetcher + ) + + if (isLoading) return
Loading...
+ if (error) return
Error: {error.message}
+ + return ( +
    + {data.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +
+
+ +## ストリーミング {#streaming} + +> **注意:** この後の内容は、アプリケーションで[`dynamicIO`設定オプション](/docs/app/api-reference/config/next-config-js/dynamicIO)が有効になっていることを前提としています。このフラグは、Next.js 15のカナリア版で導入されました。 + +Server Componentで`async/await`を使用する場合、Next.jsでは**動的レンダリング**が有効になります。これは、データがサーバーで取得され、ユーザーのリクエストごとにレンダリングされることを意味します。データリクエストが遅い場合、ルート全体がレンダリングされるのを待つことになります。 + +初期ロード時間とユーザー体験を向上させるために、ページのHTMLをより小さなチャンクに分割し、それらのチャンクをサーバーからクライアントに逐次送信することで、ストリーミングを利用できます。 + +ストリーミングによるサーバーレンダリングの仕組み + +アプリケーションでストリーミングを実装する方法は2つあります: + +1. [`loading.js`ファイル](#with-loadingjs)を使用する +2. Reactの[``コンポーネント](#with-suspense)を使用する + +### `loading.js`を使用する {#with-loading-js} + +データが取得されている間に**ページ全体**をストリームするために、ページと同じフォルダー内に`loading.js`ファイルを作成できます。例えば、`app/blog/page.js`をストリームするには、`app/blog`フォルダー内にファイルを追加します。 + +loading.jsファイルを含むブログフォルダの構造 + + + + +```tsx title="app/blog/loading.tsx" switcher +export default function Loading() { + // ここでロード中UIを定義します + return
Loading...
+} +``` + +
+ + +```jsx title="app/blog/loading.js" switcher +export default function Loading() { + // ここでロード中UIを定義します + return
Loading...
+} +``` + +
+
+ +ナビゲーション時に、ユーザーはページがレンダリングされている間にlayoutと[ロード状態](#creating-meaningful-loading-states)を見ることができます。新しいコンテンツはレンダリングが完了すると自動的に新しいものに置き換えられます。 + +ロード中のUI + +舞台裏では、`loading.js`は`layout.js`内にネストされ、`page.js`ファイルと下のすべての子を自動的に``境界でラップします。 + +loading.jsの概要 + +このアプローチは、ルートセグメント(レイアウトとページ)にうまく機能しますが、より細かいストリーミングのために``を使用できます。 + +### ``を使用する {#with-suspense} + +``を使用すると、ページのどの部分をストリームするかをより細かく制御できます。例えば、``境界の外にあるページコンテンツをすぐに表示し、境界内のブログ投稿のリストをストリームすることができます。 + + + + +```tsx title="app/blog/page.tsx" switcher +import { Suspense } from 'react' +import BlogList from '@/components/BlogList' +import BlogListSkeleton from '@/components/BlogListSkeleton' + +export default function BlogPage() { + return ( +
+ {/* このコンテンツはすぐにクライアントへ送信されます */} +
+

Welcome to the Blog

+

Read the latest posts below.

+
+
+ {/* 境界でラップされたコンテンツはストリームされます */} + }> + + +
+
+ ) +} +``` + +
+ + +```jsx title="app/blog/page.js" switcher +import { Suspense } from 'react' +import BlogList from '@/components/BlogList' +import BlogListSkeleton from '@/components/BlogListSkeleton' + +export default function BlogPage() { + return ( +
+ {/* このコンテンツはすぐにクライアントへ送信されます */} +
+

Welcome to the Blog

+

Read the latest posts below.

+
+
+ {/* 境界でラップされたコンテンツはストリームされます */} + }> + + +
+
+ ) +} +``` + +
+
+ +### 意味のあるロード状態の作成 {#creating-meaningful-loading-states} + +インスタントロード状態とは、ナビゲーション後にユーザーに即時表示されるフォールバックUIです。最高のユーザー体験のために、意義のあるロード状態を設計し、アプリが応答していることを把握できるようにすることをお勧めします。例えば、スケルトンやスピナー、カバーフォトやタイトルなどの将来の画面の小さくても意義のある部分を使用することができます。 + +開発中は、[React Devtools](https://react.dev/learn/react-developer-tools)を使用して、コンポーネントのロード状態をプレビューし、検査することができます。 diff --git a/docs/01-app/02-building-your-application/01-routing/04-linking-and-navigating.mdx b/docs/01-app/02-building-your-application/01-routing/04-linking-and-navigating.mdx index 795afc3..c2ebdab 100644 --- a/docs/01-app/02-building-your-application/01-routing/04-linking-and-navigating.mdx +++ b/docs/01-app/02-building-your-application/01-routing/04-linking-and-navigating.mdx @@ -1,26 +1,26 @@ --- title: 'リンクとナビゲーション' -description: 'Next.jsにおけるナビゲーションの仕組みと、LinkコンポーネントとuseRouterフックの使用方法を学びます' +description: 'Next.js でのナビゲーションの仕組みと、Link コンポーネントや `useRouter` フックの使い方を学びます。' related: links: - - 'app/building-your-application/caching' - - 'app/api-reference/config/typescript' + - app/building-your-application/caching + - app/api-reference/config/typescript --- -Next.jsでは、ルート間をナビゲートする方法は4つあります: +Next.js でルート間のナビゲーションを行う方法は次の4つです: -- [`` コンポーネント](#link-component)を使用する -- [`useRouter` フック](#userouter-hook)を使用する([Client Components](/docs/app/building-your-application/rendering/client-components)) -- [`redirect` 関数](#redirect-function)を使用する([Server Components](/docs/app/building-your-application/rendering/server-components)) -- ネイティブな[History API](#using-the-native-history-api)を使用する +- [`` コンポーネント](#link-component)を使う +- [`useRouter` フック](#userouter-hook)を使う([Client Components](/docs/app/building-your-application/rendering/client-components)) +- [`redirect` 関数](#redirect-function)を使う([Server Components](/docs/app/building-your-application/rendering/server-components)) +- ネイティブな [History API](#using-the-native-history-api)を使う -このページでは、それぞれのオプションの使用方法について説明し、ナビゲーションの仕組みを詳しく見ていきます。 +このページでは、これらの各オプションの使い方を説明し、ナビゲーション機能の仕組みを深く掘り下げます。 ## `` コンポーネント {#link-component} -``は、HTMLの`
`タグを拡張し、[プリフェッチ](#2-prefetching)とクライアントサイドでのルート間ナビゲーションを提供する組み込みのコンポーネントです。Next.jsでルート間をナビゲートする主な推奨方法です。 +`` は、HTML の `` タグを拡張して[プリフェッチ](#2-prefetching)やクライアントサイドのルート間ナビゲーションを提供する組み込みのコンポーネントです。Next.js でルート間のナビゲーションを行うために推奨される主な方法です。 -`next/link`からインポートし、`href`プロップをコンポーネントに渡すことで使用できます: +`next/link` からインポートし、コンポーネントに `href` prop を渡して使用します: @@ -47,11 +47,11 @@ export default function Page() { -``に渡す他のオプションの`props`もあります。詳細は[APIリファレンス](/docs/app/api-reference/components/link)をご覧ください。 +`` に渡せる他のオプションの props については、[API リファレンス](/docs/app/api-reference/components/link)をご覧ください。 ## `useRouter()` フック {#userouter-hook} -`useRouter`フックを使うと、[Client Components](/docs/app/building-your-application/rendering/client-components)からプログラム的にルートを変更できます。 +`useRouter` フックを使用すると、[Client Components](/docs/app/building-your-application/rendering/client-components)からプログラム的にルートを変更することができます。 ```jsx title="app/page.js" 'use client' @@ -69,13 +69,13 @@ export default function Page() { } ``` -`useRouter`メソッドの完全なリストについては、[APIリファレンス](/docs/app/api-reference/functions/use-router)を参照してください。 +`useRouter` メソッドの全一覧は、[API リファレンス](/docs/app/api-reference/functions/use-router)を参照してください。 -> **推奨事項:** 特定の要件がない限り、ルート間をナビゲートするためには``コンポーネントを使用してください。 +> **推奨:** 特定の `useRouter` 使用要件がない限り、ルート間のナビゲーションには `` コンポーネントを使用してください。 ## `redirect` 関数 {#redirect-function} -[Server Components](/docs/app/building-your-application/rendering/server-components)の場合、代わりに`redirect`関数を使用してください。 +[Server Components](/docs/app/building-your-application/rendering/server-components)には、代わりに `redirect` 関数を使用します。 @@ -89,12 +89,21 @@ async function fetchTeam(id: string) { return res.json() } -export default async function Profile({ params }: { params: { id: string } }) { - const team = await fetchTeam(params.id) - if (!team) { +export default async function Profile({ + params, +}: { + params: Promise<{ id: string }> +}) { + const id = (await params).id + if (!id) { redirect('/login') } + const team = await fetchTeam(id) + if (!team) { + redirect('/join') + } + // ... } ``` @@ -112,11 +121,16 @@ async function fetchTeam(id) { } export default async function Profile({ params }) { - const team = await fetchTeam(params.id) - if (!team) { + const id = (await params).id + if (!id) { redirect('/login') } + const team = await fetchTeam(id) + if (!team) { + redirect('/join') + } + // ... } ``` @@ -124,25 +138,25 @@ export default async function Profile({ params }) { -> **知っておいてよい点**: +> **Good to know**: > -> - `redirect`はデフォルトで307(Temporary Redirect)ステータスコードを返します。サーバーアクションで使用した場合、303(See Other)を返します。これは、POSTリクエストの結果として成功ページにリダイレクトする際によく使用されます。 -> - `redirect`は内部的にエラーを投げるので、`try/catch`ブロックの外で呼び出されるべきです。 -> - `redirect`はクライアントコンポーネントでのレンダリングプロセス中に呼び出せますが、イベントハンドラ内では使用できません。代わりに[`useRouter`フック](#userouter-hook)を使用できます。 -> - `redirect`は絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます。 -> - レンダープロセスの前にリダイレクトしたい場合は、[`next.config.js`](/docs/app/building-your-application/routing/redirecting#redirects-in-nextconfigjs)や[ミドルウェア](/docs/app/building-your-application/routing/redirecting#nextresponseredirect-in-middleware)を使用してください。 +> - `redirect` はデフォルトで 307(Temporary Redirect)ステータスコードを返します。Server Action で使用された場合、POST リクエストの結果として成功ページへのリダイレクトに一般的に使用される 303(See Other)を返します。 +> - `redirect` は内部でエラーをスローするため、`try/catch` ブロックの外部で呼び出す必要があります。 +> - `redirect` は、Client Components のレンダリング処理中に呼び出すことができますが、イベントハンドラ内では呼び出せません。代わりに [`useRouter` フック](#userouter-hook)を使用できます。 +> - `redirect` は絶対 URL も受け入れることができ、外部リンクへのリダイレクトに使用できます。 +> - レンダリングプロセスの前にリダイレクトしたい場合は、[`next.config.js`](/docs/app/building-your-application/routing/redirecting#redirects-in-nextconfigjs) または [Middleware](/docs/app/building-your-application/routing/redirecting#nextresponseredirect-in-middleware) を使用します。 -より詳しい情報は、[`redirect` APIリファレンス](/docs/app/api-reference/functions/redirect)を参照してください。 +詳しくは [`redirect` API リファレンス](/docs/app/api-reference/functions/redirect)をご覧ください。 -## ネイティブなHistory APIを使用する {#using-the-native-history-api} +## ネイティブな History API の使用 {#using-the-native-history-api} -Next.jsでは、ネイティブの[`window.history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)と[`window.history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)メソッドを使用して、ページをリロードせずにブラウザの履歴スタックを更新することができます。 +Next.js では、ページをリロードせずにブラウザの履歴スタックを更新するために、ネイティブな [`window.history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) と [`window.history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState) メソッドを使用することができます。 -`pushState`と`replaceState`の呼び出しはNext.js Routerと統合され、[`usePathname`](/docs/app/api-reference/functions/use-pathname)と[`useSearchParams`](/docs/app/api-reference/functions/use-search-params)と同期できます。 +`pushState` と `replaceState` の呼び出しは Next.js Router に統合されており、[`usePathname`](/docs/app/api-reference/functions/use-pathname) や [`useSearchParams`](/docs/app/api-reference/functions/use-search-params) と同期できます。 ### `window.history.pushState` {#window-history-pushstate} -ブラウザの履歴スタックに新しいエントリを追加するために使用します。ユーザーは前の状態に戻ることができます。例えば、製品リストをソートするには: +ブラウザの履歴スタックに新しいエントリを追加するために使用します。ユーザーは前の状態に戻ることができます。たとえば、製品リストをソートする: @@ -201,7 +215,7 @@ export default function SortProducts() { ### `window.history.replaceState` {#window-history-replacestate} -ブラウザの履歴スタックの現在のエントリを置き換えるために使用します。ユーザーは前の状態に戻ることができません。例えば、アプリケーションのロケールを切り替えるには: +ブラウザの履歴スタックの現在のエントリを置き換えるために使用します。ユーザーは前の状態に戻ることができません。たとえば、アプリケーションのロケールを切り替える: @@ -215,7 +229,7 @@ export function LocaleSwitcher() { const pathname = usePathname() function switchLocale(locale: string) { - // e.g. '/en/about' or '/fr/contact' + // 例:'/en/about' または '/fr/contact' const newPath = `/${locale}${pathname}` window.history.replaceState(null, '', newPath) } @@ -241,7 +255,7 @@ export function LocaleSwitcher() { const pathname = usePathname() function switchLocale(locale) { - // e.g. '/en/about' or '/fr/contact' + // 例:'/en/about' または '/fr/contact' const newPath = `/${locale}${pathname}` window.history.replaceState(null, '', newPath) } @@ -260,67 +274,67 @@ export function LocaleSwitcher() { ## ルーティングとナビゲーションの仕組み {#how-routing-and-navigation-works} -App Routerは、ルーティングとナビゲーションにハイブリッドアプローチを採用しています。サーバー上では、アプリケーションコードがルートセグメントによって自動的に[コード分割](#1-code-splitting)され、クライアント側では、Next.jsがルートセグメントを[プリフェッチ](#2-prefetching)し[キャッシュ](#3-caching)します。つまり、ユーザーが新しいルートに移動しても、ブラウザはページをリロードせず、変更されたルートセグメントだけが再レンダリングされます。これにより、ナビゲーション体験とパフォーマンスが向上します。 +App Router は、ルーティングとナビゲーションにハイブリッドアプローチを採用しています。サーバーでは、アプリケーションコードがルートセグメントごとに自動的に[コード分割](#1-code-splitting)されます。そしてクライアントでは、Next.js がルートセグメントを[プリフェッチ](#2-prefetching)し、[キャッシュ](#3-caching)します。これにより、ユーザーが新しいルートにナビゲートするときにブラウザはページをリロードせず、変更されたルートセグメントだけが再レンダリングされます。これによりナビゲーション体験とパフォーマンスが向上します。 ### 1. コード分割 {#1-code-splitting} -コード分割により、アプリケーションコードを小さなバンドルに分割し、ブラウザでダウンロードおよび実行できるようになります。これにより、データ転送量と各リクエストの実行時間が短縮され、パフォーマンスが向上します。 +コード分割を使用すると、アプリケーションコードをより小さなバンドルに分割し、ブラウザによってダウンロードおよび実行されるようにできます。これにより、各リクエストのデータ転送量と実行時間が削減され、パフォーマンスが向上します。 -[Server Components](/docs/app/building-your-application/rendering/server-components)により、アプリケーションコードはルートセグメントによって自動的にコード分割されます。これにより、ナビゲーション時には現在のルートに必要なコードのみが読み込まれます。 +[Server Components](/docs/app/building-your-application/rendering/server-components)は、アプリケーションコードをルートセグメントごとに自動的にコード分割することができます。これにより、ナビゲーション時にそのルートに必要なコードだけがロードされます。 ### 2. プリフェッチ {#2-prefetching} -プリフェッチは、ユーザーが訪問する前にバックグラウンドでルートを事前にロードする方法です。 +プリフェッチは、ユーザーが訪れる前にバックグラウンドでルートをプレロードする方法です。 -Next.jsではルートがプリフェッチされる方法は2つあります: +Next.js では2つの方法でプリフェッチが行われます: -- **`` コンポーネント**:ユーザーのビューポートに表示されると、ルートは自動的にプリフェッチされます。プリフェッチは、ページが最初にロードされるときや、スクロールによってビューに入るときに行われます。 -- **`router.prefetch()`**:`useRouter`フックを使用して、プログラム的にルートをプリフェッチできます。 +- **`` コンポーネント**: ルートはユーザーのビューポートに表示されたときに自動的にプリフェッチされます。プリフェッチは、ページが最初に読み込まれたときやスクロールして表示されるときに行われます。 +- **`router.prefetch()`**: `useRouter` フックを使用してプログラム的にルートをプリフェッチできます。 -``のデフォルトのプリフェッチ動作(つまり、`prefetch`プロップが未指定または`null`に設定されている場合)は、[`loading.js`](/docs/app/api-reference/file-conventions/loading)の使用に応じて異なります。共有レイアウトのみがプリフェッチされ、「コンポーネントのツリー」がレンダリングされる最初の`loading.js`ファイルまでキャッシュされます。これにより、動的ルート全体をフェッチするコストが削減され、即時の[ローディング状態](/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states)がユーザーにより良い視覚的フィードバックを提供します。 +`` のデフォルトのプリフェッチ動作(`prefetch` prop が未指定または `null` に設定されている場合)は、[`loading.js`](/docs/app/api-reference/file-conventions/loading) の使用方法によって異なります。プリフェッチされ、キャッシュされるのは、最初の `loading.js` ファイルまでのレンダリングされた "tree" の共有レイアウトだけで `30秒` の間キャッシュされます。この方法は、動的ルート全体をフェッチするコストを削減し、ユーザーにより良い視覚的フィードバックを与えるための[即時のローディング状態](/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states)を表示することができます。 -プリフェッチを無効にするには、`prefetch`プロップを`false`に設定します。あるいは、`prefetch`プロップを`true`に設定して、ローディングの境界を超えて完全なページデータをプリフェッチすることができます。 +プリフェッチを無効にするには、`prefetch` prop を `false` に設定します。また、`prefetch` prop を `true` に設定することで、ローディング境界を超えて完全なページデータをプリフェッチすることもできます。 -詳細は[`` APIリファレンス](/docs/app/api-reference/components/link)を参照してください。 +詳しくは [`` API リファレンス](/docs/app/api-reference/components/link)をご覧ください。 -> **知っておいてよい点**: +> **Good to know**: > -> - プリフェッチは開発環境では有効ではなく、本番環境でのみ有効です。 +> - 開発環境ではプリフェッチは有効ではなく、本番環境でのみ有効です。 -### 3. キャッシュ {#3-caching} +### 3. キャッシング {#3-caching} -Next.jsには**インメモリーのクライアントサイドキャッシュ**があり、[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache)と呼ばれます。ユーザーがアプリをナビゲートすると、[プリフェッチされた](#2-prefetching)ルートセグメントと訪問したルートのReactサーバーコンポーネントペイロードがキャッシュに保存されます。 +Next.js には、[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache) と呼ばれる**インメモリのクライアントサイドキャッシュ**があります。ユーザーがアプリをナビゲートする際、[プリフェッチされた](#2-prefetching)ルートセグメントと訪問したルートの React Server Component Payload がキャッシュに格納されます。 -つまり、ナビゲーション時には、可能な限りキャッシュが再利用され、新たなサーバーへのリクエストを行う代わりに、キャッシュが使用されます。これにより、リクエスト数とデータ転送量が削減され、パフォーマンスが向上します。 +これにより、ナビゲーション時に新たなサーバーリクエストを減らし、キャッシュをできる限り再利用することが可能です。これはリクエストとデータ転送量を削減し、パフォーマンスの向上につながります。 -[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache)の仕組みと構成方法についての詳細をご覧ください。 +[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache)の仕組みと設定方法について詳しく学びましょう。 -### 4. 部分的なレンダリング {#4-partial-rendering} +### 4. 部分レンダリング {#4-partial-rendering} -部分的なレンダリングとは、クライアント上のナビゲーションで変更されるルートセグメントのみが再レンダリングされ、共有セグメントは保持されることを意味します。 +部分レンダリングとは、ナビゲーション時に変更があったルートセグメントのみをクライアントで再レンダリングし、共有されているセグメントを保持することを意味します。 -たとえば、2つの兄弟ルート`/dashboard/settings`と`/dashboard/analytics`間を移動する際には、`settings`ページがアンマウントされ、`analytics`ページが新しい状態でマウントされ、共有の`dashboard`レイアウトが保存されます。この動作は、同じ動的セグメント上の2つのルート間でも発生します。たとえば、`/blog/[slug]/page`および`/blog/first`から`/blog/second`へのナビゲーション時です。 +たとえば、2つの兄弟ルート `/dashboard/settings` と `/dashboard/analytics` の間を移動する際、`settings` ページはアンマウントされ、`analytics` ページが新しい状態でマウントされ、共有された `dashboard` レイアウトは保持されます。この動作は、同じ動的セグメント上の2つのルート間、例として `/blog/[slug]/page` から `/blog/first` から `/blog/second` にナビゲートする場合でも存在します。 部分的なレンダリングの仕組み -部分的なレンダリングがない場合、各ナビゲーションはクライアントですべてのページを再レンダリングさせます。変更されるセグメントのみをレンダリングすることで、データ転送量と実行時間が削減され、パフォーマンスが向上します。 +部分レンダリングがなければ、各ナビゲーションでクライアント上でフルページを再レンダリングすることになります。変更があったセグメントだけをレンダリングすることで、データ転送量と実行時間を削減し、パフォーマンス向上に寄与します。 ### 5. ソフトナビゲーション {#5-soft-navigation} -ブラウザはページ間のナビゲーションで「ハードナビゲーション」を行います。Next.jsのApp Routerは、ページ間での「ソフトナビゲーション」を可能にし、変更されたルートセグメントのみが再レンダリングされるようにします(部分的なレンダリング)。これにより、クライアントのReact stateがナビゲーション中に保存されます。 +ブラウザは、ページ間のナビゲーション時に「ハードナビゲーション」を行います。Next.js の App Router は、ページ間で「ソフトナビゲーション」を可能にし、変更されたルートセグメントのみを再レンダリング(部分レンダリング)します。これにより、ナビゲーション中にクライアントの React 状態を保持できます。 -### 6. 前後のナビゲーション {#6-back-and-forward-navigation} +### 6. 前進および後進ナビゲーション {#6-back-and-forward-navigation} -デフォルトでは、Next.jsは前後のナビゲーションでスクロール位置を維持し、[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache)のルートセグメントを再利用します。 +デフォルトで、Next.js は前進および後進ナビゲーション用のスクロール位置を保持し、[Router Cache](/docs/app/building-your-application/caching#client-side-router-cache) でルートセグメントを再利用します。 -### 7. `pages/`と`app/`間のルーティング {#7-routing-between-pages-and-app} +### 7. `pages/` と `app/` 間でのルーティング {#7-routing-between-pages-and-app} -`pages/`から`app/`へのインクリメンタル移行時に、Next.jsルーターは両者間のハードナビゲーションを自動的に処理します。`pages/`から`app/`への移行を検出するために、アプリルートの確率的チェックを活用したクライアントルーターフィルターがありますが、これは稀に偽陽性を引き起こす可能性があります。通常、こうした事例は非常に稀ですが、偽陽性の確率は0.01%に設定されています。この確率は、`next.config.js`の`experimental.clientRouterFilterAllowedRate`オプションを通じてカスタマイズできます。偽陽性率を下げると、クライアントバンドル内の生成されたフィルターのサイズが増加することに注意してください。 +`pages/` から `app/` への増分移行時、Next.js の router はこの2つの間でのハードナビゲーションを自動的に処理します。クライアントルーターのフィルタのアプリルートの確率的チェックを活用し、`pages/` から `app/` への遷移を検出します。これにより稀に誤検知が発生します。デフォルトでこの発生は非常に稀で、誤検知の確率を 0.01% に設定しています。この確率は、`next.config.js` の `experimental.clientRouterFilterAllowedRate` オプションでカスタマイズできます。誤検知率を下げると、クライアントバンドル内で生成されたフィルタサイズが増加することに注意が必要です。 -完全にこの処理を無効にして、`pages/`と`app/`間のルーティングを手動で管理したい場合は、`next.config.js`で`experimental.clientRouterFilter`を`false`に設定できます。この機能が無効化されると、appルートと重なるページの動的ルートがデフォルトでは正しくナビゲートされません。 +また、`experimental.clientRouterFilter` を `false` に設定すると、この処理を完全に無効にして、`pages/` と `app/` 間のルーティングを手動で管理することもできます。これにより、デフォルトでは app ルートと重複する `pages/` 内の動的ルートには、適切なナビゲーションが行われません。 diff --git a/docs/01-app/02-building-your-application/01-routing/07-redirecting.mdx b/docs/01-app/02-building-your-application/01-routing/07-redirecting.mdx index 8fe8461..ab73ad8 100644 --- a/docs/01-app/02-building-your-application/01-routing/07-redirecting.mdx +++ b/docs/01-app/02-building-your-application/01-routing/07-redirecting.mdx @@ -9,27 +9,27 @@ related: - 'app/api-reference/config/next-config-js/redirects' --- -Next.jsでリダイレクトを処理する方法はいくつかあります。このページでは、利用可能な各オプション、ユースケース、および大量のリダイレクトを管理する方法について説明します。 +Next.jsではリダイレクトを処理する方法がいくつかあります。このページでは、利用可能な各オプション、使用事例、大量のリダイレクトを管理する方法について説明します。 -| API | 目的 | 使用場所 | ステータスコード | -| -------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------- | ----------------------------------- | -| [`redirect`](#redirect-function) | ミューテーションまたはイベント後にユーザーをリダイレクトする | Server Components、Server Actions、Route Handlers | 307(仮)または303(Server Action) | -| [`permanentRedirect`](#permanentredirect-function) | ミューテーションまたはイベント後にユーザーをリダイレクトする | Server Components、Server Actions、Route Handlers | 308(永続) | -| [`useRouter`](#userouter-hook) | クライアントサイドのナビゲーションを実行する | Client Componentsのイベントハンドラーで | 該当なし | -| [`next.config.js`内の`redirects`](#redirects-in-nextconfigjs) | パスに基づいて受信リクエストをリダイレクトする | `next.config.js`ファイル | 307(仮)または308(永続) | -| [`NextResponse.redirect`](#nextresponseredirect-in-middleware) | 条件に基づいて受信リクエストをリダイレクトする | ミドルウェア | 任意 | +| API | 目的 | 使用場所 | ステータスコード | +| -------------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------- | ------------------------------------------ | +| [`redirect`](#redirect-function) | ミューテーションやイベント後にユーザーをリダイレクト | Server Components, Server Actions, Route Handlers | 307(Temporary)または303(Server Action) | +| [`permanentRedirect`](#permanentredirect-function) | ミューテーションやイベント後にユーザーをリダイレクト | Server Components, Server Actions, Route Handlers | 308(Permanent) | +| [`useRouter`](#userouter-hook) | クライアントサイドのナビゲーションを実行 | Event Handlers in Client Components | N/A | +| [`next.config.js`の`redirects`](#redirects-in-nextconfigjs) | パスに基づいて受信リクエストをリダイレクト | `next.config.js` ファイル | 307(Temporary)または308(Permanent) | +| [`NextResponse.redirect`](#nextresponseredirect-in-middleware) | 条件に基づいて受信リクエストをリダイレクト | Middleware | Any | -| API | 目的 | 使用場所 | ステータスコード | -| -------------------------------------------------------------- | ---------------------------------------------- | ------------------------ | -------------------------- | -| [`useRouter`](#userouter-hook) | クライアントサイドのナビゲーションを実行する | コンポーネント | 該当なし | -| [`next.config.js`内の`redirects`](#redirects-in-nextconfigjs) | パスに基づいて受信リクエストをリダイレクトする | `next.config.js`ファイル | 307(仮)または308(永続) | -| [`NextResponse.redirect`](#nextresponseredirect-in-middleware) | 条件に基づいて受信リクエストをリダイレクトする | ミドルウェア | 任意 | +| API | 目的 | 使用場所 | ステータスコード | +| -------------------------------------------------------------- | ------------------------------------------ | ------------------------- | -------------------------------------- | +| [`useRouter`](#userouter-hook) | クライアントサイドのナビゲーションを実行 | Components | N/A | +| [`next.config.js`の`redirects`](#redirects-in-nextconfigjs) | パスに基づいて受信リクエストをリダイレクト | `next.config.js` ファイル | 307(Temporary)または308(Permanent) | +| [`NextResponse.redirect`](#nextresponseredirect-in-middleware) | 条件に基づいて受信リクエストをリダイレクト | Middleware | Any | @@ -37,14 +37,14 @@ Next.jsでリダイレクトを処理する方法はいくつかあります。 ## `redirect` 関数 {#redirect-function} -`redirect` 関数を使用すると、ユーザーを別のURLにリダイレクトすることができます。`redirect`を[Server Components](/docs/app/building-your-application/rendering/server-components)、[Route Handlers](/docs/app/building-your-application/routing/route-handlers)、および[Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)で呼び出すことができます。 +`redirect` 関数を使用すると、ユーザーを別のURLにリダイレクトすることができます。`redirect` を [Server Components](/docs/app/building-your-application/rendering/server-components)、[Route Handlers](/docs/app/building-your-application/routing/route-handlers)、または[Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)で呼び出すことができます。 -`redirect`は、ミューテーションまたはイベントの後によく使用されます。たとえば、投稿を作成する場合です: +`redirect` は通常、ミューテーションやイベント後に使用されます。たとえば、投稿を作成する場合: - + -```tsx title="app/actions.tsx" switcher +```ts title="app/actions.ts" switcher 'use server' import { redirect } from 'next/navigation' @@ -52,20 +52,20 @@ import { revalidatePath } from 'next/cache' export async function createPost(id: string) { try { - // データベースを呼び出す + // Call database } catch (error) { - // エラーを処理する + // エラーを処理 } - revalidatePath('/posts') // キャッシュされた投稿を更新する - redirect(`/post/${id}`) // 新しい投稿ページに移動する + revalidatePath('/posts') // キャッシュされた投稿を更新 + redirect(`/post/${id}`) // 新しい投稿ページに移動 } ``` - + -```jsx title="app/actions.js" switcher +```js title="app/actions.js" switcher 'use server' import { redirect } from 'next/navigation' @@ -73,39 +73,39 @@ import { revalidatePath } from 'next/cache' export async function createPost(id) { try { - // データベースを呼び出す + // Call database } catch (error) { - // エラーを処理する + // エラーを処理 } - revalidatePath('/posts') // キャッシュされた投稿を更新する - redirect(`/post/${id}`) // 新しい投稿ページに移動する + revalidatePath('/posts') // キャッシュされた投稿を更新 + redirect(`/post/${id}`) // 新しい投稿ページに移動 } ``` -> **参考情報**: +> **Good to know**: > -> - `redirect`はデフォルトで307(Temporary Redirect)ステータスコードを返します。Server Actionで使用されるとき、通常はPOSTリクエストの結果として成功ページにリダイレクトするために使用される303(See Other)を返します。 -> - `redirect`は内部でエラーをスローするため、`try/catch`ブロックの外で呼び出す必要があります。 -> - `redirect`はクライアントコンポーネントのレンダリングプロセス中に呼び出すことができますが、イベントハンドラーでは呼び出せません。[`useRouter`フック](#userouter-hook)を代わりに使用できます。 -> - `redirect`は絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます。 -> - レンダープロセスの前にリダイレクトしたい場合は、[`next.config.js`](#redirects-in-nextconfigjs)または[Middleware](#nextresponseredirect-in-middleware)を使用してください。 +> - `redirect` はデフォルトで307(Temporary Redirect)ステータスコードを返します。Server Actionで使用すると、POSTリクエストの結果として成功ページにリダイレクトする際によく使用される303(See Other)を返します。 +> - `redirect` は内部でエラーをスローするため、`try/catch` ブロックの外で呼び出す必要があります。 +> - `redirect`はClient Componentsのレンダリングプロセス中に呼び出すことができますが、イベントハンドラ内では呼び出せません。代わりに [`useRouter` フック](#userouter-hook)を使用できます。 +> - `redirect` は絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます。 +> - レンダリングプロセスの前にリダイレクトしたい場合は、 [`next.config.js`](#redirects-in-nextconfigjs) または[Middleware](#nextresponseredirect-in-middleware)を使用してください。 -[`redirect` APIリファレンス](/docs/app/api-reference/functions/redirect)を参照して、さらに情報をご覧ください。 +詳しくは、[`redirect` APIリファレンス](/docs/app/api-reference/functions/redirect)をご覧ください。 ## `permanentRedirect` 関数 {#permanentredirect-function} -`permanentRedirect` 関数は、ユーザーを別のURLに**永久に**リダイレクトすることができます。`permanentRedirect`を[Server Components](/docs/app/building-your-application/rendering/server-components)、[Route Handlers](/docs/app/building-your-application/routing/route-handlers)、および[Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)で呼び出すことができます。 +`permanentRedirect` 関数を使用すると、ユーザーを**永久に**別のURLにリダイレクトすることができます。 `permanentRedirect`を [Server Components](/docs/app/building-your-application/rendering/server-components)、[Route Handlers](/docs/app/building-your-application/routing/route-handlers)、または [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)で呼び出すことができます。 -`permanentRedirect`は、エンティティの正規URLが変更されるミューテーションまたはイベントの後によく使用されます。たとえば、ユーザーがユーザー名を変更した後にプロフィールURLを更新する場合です: +`permanentRedirect` は通常、ユーザーがユーザー名を変更した後にプロフィールURLを更新するなど、エンティティの正規URLが変更された後に使用されます。 - + -```tsx title="app/actions.ts" switcher +```ts title="app/actions.ts" switcher 'use server' import { permanentRedirect } from 'next/navigation' @@ -113,20 +113,20 @@ import { revalidateTag } from 'next/cache' export async function updateUsername(username: string, formData: FormData) { try { - // データベースを呼び出す + // Call database } catch (error) { - // エラーを処理する + // エラーを処理 } - revalidateTag('username') // ユーザー名のすべての参照を更新する - permanentRedirect(`/profile/${username}`) // 新しいユーザープロフィールに移動する + revalidateTag('username') // ユーザー名へのすべての参照を更新 + permanentRedirect(`/profile/${username}`) // 新しいユーザープロフィールに移動 } ``` - + -```jsx title="app/actions.js" switcher +```js title="app/actions.js" switcher 'use server' import { permanentRedirect } from 'next/navigation' @@ -134,26 +134,26 @@ import { revalidateTag } from 'next/cache' export async function updateUsername(username, formData) { try { - // データベースを呼び出す + // Call database } catch (error) { - // エラーを処理する + // エラーを処理 } - revalidateTag('username') // ユーザー名のすべての参照を更新する - permanentRedirect(`/profile/${username}`) // 新しいユーザープロフィールに移動する + revalidateTag('username') // ユーザー名へのすべての参照を更新 + permanentRedirect(`/profile/${username}`) // 新しいユーザープロフィールに移動 } ``` -> **参考情報**: +> **Good to know**: > -> - `permanentRedirect`はデフォルトで308(恒久的リダイレクト)ステータスコードを返します; -> - `permanentRedirect`は絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます; -> - レンダープロセスの前にリダイレクトしたい場合は、[`next.config.js`](#redirects-in-nextconfigjs)または[Middleware](#nextresponseredirect-in-middleware)を使用してください; +> - `permanentRedirect` はデフォルトで308(Permanent Redirect)ステータスコードを返します。 +> - `permanentRedirect` は絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます。 +> - レンダリングプロセスの前にリダイレクトしたい場合は、[`next.config.js`](#redirects-in-nextconfigjs) または [Middleware](#nextresponseredirect-in-middleware)を使用してください。 -[`permanentRedirect` APIリファレンス](/docs/app/api-reference/functions/permanentRedirect)を参照して、さらに情報をご覧ください。 +詳しくは、[`permanentRedirect` APIリファレンス](/docs/app/api-reference/functions/permanentRedirect)をご覧ください。 @@ -161,7 +161,7 @@ export async function updateUsername(username, formData) { -クライアントコンポーネントのイベントハンドラー内でリダイレクトが必要な場合、`useRouter`フックの`push`メソッドを使用できます。例えば: +Client Componentのイベントハンドラ内でリダイレクトを行う必要がある場合、`useRouter` フックから `push` メソッドを使用できます。たとえば: @@ -208,7 +208,7 @@ export default function Page() { -コンポーネント内でリダイレクトする必要がある場合、`useRouter`フックの`push`メソッドを使用できます。例えば: +コンポーネント内でリダイレクトを実行する必要がある場合、`useRouter` フックから `push` メソッドを使用できます。たとえば: @@ -249,29 +249,29 @@ export default function Page() { -> **参考情報**: +> **Good to know**: > -> - プログラムでユーザーをナビゲートする必要がない場合は、[``](/docs/app/api-reference/components/link)コンポーネントを使用するべきです。 +> - ユーザーをプログラムでナビゲートする必要がない場合は、[``](/docs/app/api-reference/components/link)コンポーネントを使用すべきです。 -[`useRouter` APIリファレンス](/docs/app/api-reference/functions/use-router)を参照して、さらに情報をご覧ください。 +詳しくは、[`useRouter` APIリファレンス](/docs/app/api-reference/functions/use-router)をご覧ください。 -[`useRouter` APIリファレンス](https://nextjs.org/docs/canary/pages/api-reference/functions/use-router)を参照して、さらに情報をご覧ください。 +詳しくは、[`useRouter` APIリファレンス](https://nextjs.org/docs/canary/pages/api-reference/functions/use-router)をご覧ください。 -## `next.config.js`内の`redirects` {#redirects-in-next-config-js} +## `next.config.js`の`redirects` {#redirects-in-next-config-js} -`next.config.js`ファイル内の`redirects`オプションを使用すると、受信リクエストのパスを別の宛先パスにリダイレクトできます。これは、ページのURL構造を変更した場合や、事前に既知のリダイレクトのリストがある場合に便利です。 +`next.config.js` ファイルの `redirects` オプションを使用すると、受信リクエストのパスを異なる宛先パスにリダイレクトできます。これは、ページのURL構造を変更したり、事前に既知のリダイレクトのリストを持っている場合に便利です。 -`redirects`は、[パス](/docs/app/api-reference/config/next-config-js/redirects#path-matching)、[ヘッダー、cookie、クエリのマッチング](/docs/app/api-reference/config/next-config-js/redirects#header-cookie-and-query-matching)をサポートし、受信リクエストに基づいてユーザーをリダイレクトする柔軟性を提供します。 +`redirects` は [パス](/docs/app/api-reference/config/next-config-js/redirects#path-matching)、[ヘッダー、cookie、およびクエリのマッチング](/docs/app/api-reference/config/next-config-js/redirects#header-cookie-and-query-matching)をサポートしており、受信リクエストに基づいてユーザーをリダイレクトする柔軟性を提供します。 -`redirects`を使用するには、`next.config.js`ファイルにオプションを追加します: +`redirects` を使用するには、`next.config.js` ファイルにオプションを追加します: ```js title="next.config.js" module.exports = { @@ -294,36 +294,36 @@ module.exports = { } ``` -[`redirects` APIリファレンス](/docs/app/api-reference/config/next-config-js/redirects)を参照して、さらに情報をご覧ください。 +詳しくは、[`redirects` APIリファレンス](/docs/app/api-reference/config/next-config-js/redirects)をご覧ください。 -> **参考情報**: +> **Good to know**: > -> - `redirects`は、`permanent`オプションを使用して307(Temporary Redirect)または308(Permanent Redirect)ステータスコードを返すことができます; -> - プラットフォームによっては、`redirects`に制限がある場合があります。たとえば、Vercelでは1,024件のリダイレクトの制限があります。大量のリダイレクト(1000件以上)を管理するには、[Middleware](/docs/app/building-your-application/routing/middleware)を使用したカスタムソリューションを作成することを検討してください。規模に応じたリダイレクト管理については、[managing redirects at scale](#managing-redirects-at-scale-advanced)を参照してください; -> - `redirects`はミドルウェアの**前に**実行されます; +> - `redirects` は `permanent` オプションで 307(Temporary Redirect)または 308(Permanent Redirect)ステータスコードを返すことができます。 +> - `redirects` はプラットフォーム上に制限がある場合があります。たとえば、Vercelでは、リダイレクトの最大数が1,024に制限されています。大量のリダイレクト(1000以上)を管理するには、[Middleware](/docs/app/building-your-application/routing/middleware)を使用してカスタムソリューションを作成することを検討してください。大規模でのリダイレクトの管理については、[こちら](#managing-redirects-at-scale-advanced)をご覧ください。 +> - `redirects` は **Middlewareよりも前に** 実行されます。 -## `NextResponse.redirect` in Middleware {#nextresponse-redirect-in-middleware} +## Middleware 内の `NextResponse.redirect` {#nextresponse-redirect-in-middleware} -ミドルウェアを使用すると、リクエストが完了する前にコードを実行できます。そして、受信リクエストに基づいて`NextResponse.redirect`を使用して別のURLにリダイレクトします。これは、条件(例:認証、セッション管理など)に基づいてユーザーをリダイレクトする場合や、大量のリダイレクトがあります: +Middleware を使用すると、リクエストが完了する前にコードを実行できます。その後、受信リクエストに基づいて `NextResponse.redirect` を使用して別のURLにリダイレクトします。これは、ユーザーを条件に基づいてリダイレクトしたい場合(例:認証、セッション管理など)や[大規模な数のリダイレクト](#managing-redirects-at-scale-advanced)がある場合に便利です。 -たとえば、ユーザーが認証されていない場合に`/login`ページにリダイレクトするには: +たとえば、ユーザーが認証されていない場合に `/login` ページにリダイレクトするには: - + -```tsx title="middleware.ts" switcher +```ts title="middleware.ts" switcher import { NextResponse, NextRequest } from 'next/server' import { authenticate } from 'auth-provider' export function middleware(request: NextRequest) { const isAuthenticated = authenticate(request) - // ユーザーが認証されている場合は、通常通り続行します + // ユーザーが認証されている場合は通常どおり続行 if (isAuthenticated) { return NextResponse.next() } - // 認証されていない場合は、ログインページにリダイレクトします + // 認証されていない場合はログインページにリダイレクト return NextResponse.redirect(new URL('/login', request.url)) } @@ -342,12 +342,12 @@ import { authenticate } from 'auth-provider' export function middleware(request) { const isAuthenticated = authenticate(request) - // ユーザーが認証されている場合は、通常通り続行します + // ユーザーが認証されている場合は通常どおり続行 if (isAuthenticated) { return NextResponse.next() } - // 認証されていない場合は、ログインページにリダイレクトします + // 認証されていない場合はログインページにリダイレクト return NextResponse.redirect(new URL('/login', request.url)) } @@ -359,28 +359,28 @@ export const config = { -> **参考情報**: +> **Good to know**: > -> - ミドルウェアは、`next.config.js`の`redirects`**後に**実行され、レンダリングの**前に**実行されます; +> - Middleware は `next.config.js` の `redirects` の後、レンダリングより前に実行されます。 -[Middleware](/docs/app/building-your-application/routing/middleware)のドキュメントを参照して、さらに情報をご覧ください。 +詳しくは、[Middleware ドキュメント](/docs/app/building-your-application/routing/middleware)をご覧ください。 -## 規模に応じたリダイレクト管理(高度) {#managing-redirects-at-scale-advanced} +## 大規模でのリダイレクトの管理(高度な方法) {#managing-redirects-at-scale-advanced} -大量のリダイレクト(1000件以上)を管理するために、ミドルウェアを使用してカスタムソリューションを作成することを検討してください。これにより、アプリケーションを再デプロイすることなく、プログラムでリダイレクトを処理できます。 +大量のリダイレクト(1000以上)を管理するには、Middleware を使用してカスタムソリューションを作成することを検討してください。これにより、アプリケーションを再デプロイすることなく、プログラム的にリダイレクトを処理することができます。 -これを行うには、以下を考慮する必要があります: +これを行うには、次のことを考慮する必要があります: 1. リダイレクトマップの作成と保存 2. データ検索パフォーマンスの最適化 -> **Next.jsの例**:以下の推奨事項の実装については、[Middleware with Bloom filter](https://redirects-bloom-filter.vercel.app/)の例を参照してください。 +> **Next.js Example**: 以下の推奨事項を実装した [Middleware with Bloom filter](https://redirects-bloom-filter.vercel.app/) の例をご覧ください。 ### 1. リダイレクトマップの作成と保存 {#1-creating-and-storing-a-redirect-map} -リダイレクトマップは、データベース(通常はキー・バリュー・ストア)またはJSONファイルに保存できるリダイレクトのリストです。 +リダイレクトマップは、データベース(通常はキー値ストア)やJSONファイルに保存することができるリダイレクトのリストです。 -次のデータ構造を考慮してください: +以下のデータ構造を検討してください: ```json { @@ -395,12 +395,12 @@ export const config = { } ``` -[Middleware](/docs/app/building-your-application/routing/middleware)で、Vercelの[Edge Config](https://vercel.com/docs/storage/edge-config/get-started?utm_source=next-site&utm_medium=docs&utm_campaign=next-website)や[Redis](https://vercel.com/docs/storage/vercel-kv?utm_source=next-site&utm_medium=docs&utm_campaign=next-website)などのデータベースから読み取り、受信リクエストに基づいてユーザーをリダイレクトすることができます: +[Middleware](/docs/app/building-your-application/routing/middleware) で、Vercelの [Edge Config](https://vercel.com/docs/storage/edge-config/get-started?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) や [Redis](https://vercel.com/docs/storage/vercel-kv?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) などのデータベースから読み取り、受信リクエストに基づいてユーザーをリダイレクトします: - + -```tsx title="middleware.ts" switcher +```ts title="middleware.ts" switcher import { NextResponse, NextRequest } from 'next/server' import { get } from '@vercel/edge-config' @@ -419,7 +419,7 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(redirectEntry.destination, statusCode) } - // リダイレクトが見つからなかった場合、そのまま継続します + // リダイレクトが見つからなかったのでリダイレクトせずに続行 return NextResponse.next() } ``` @@ -441,7 +441,7 @@ export async function middleware(request) { return NextResponse.redirect(redirectEntry.destination, statusCode) } - // リダイレクトが見つからなかった場合、そのまま継続します + // リダイレクトが見つからなかったのでリダイレクトせずに続行 return NextResponse.next() } ``` @@ -451,19 +451,19 @@ export async function middleware(request) { ### 2. データ検索パフォーマンスの最適化 {#2-optimizing-data-lookup-performance} -大規模データセットをすべての受信リクエストのために読み取ることは遅く、費用がかかります。データ検索パフォーマンスを最適化する方法は2つあります: +受信リクエストごとに大規模なデータセットを読み込むのは遅くてコストがかかります。データ検索パフォーマンスを最適化する方法は2つあります: -- [Vercel Edge Config](https://vercel.com/docs/storage/edge-config/get-started?utm_source=next-site&utm_medium=docs&utm_campaign=next-website)や[Redis](https://vercel.com/docs/storage/vercel-kv?utm_source=next-site&utm_medium=docs&utm_campaign=next-website)などの、高速読み取りに最適化されたデータベースを使用します; -- [ブルームフィルター](https://en.wikipedia.org/wiki/Bloom_filter)のようなデータ検索戦略を使用して、リダイレクトが存在するかどうかを効率的にチェックし、より大きなリダイレクトファイルやデータベースを読み取る**前に**行います; +- 高速な読み取りに最適化されたデータベースを使用する。たとえば[Vercel Edge Config](https://vercel.com/docs/storage/edge-config/get-started?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) や [Redis](https://vercel.com/docs/storage/vercel-kv?utm_source=next-site&utm_medium=docs&utm_campaign=next-website)を使用します。 +- [ブルームフィルタ](https://en.wikipedia.org/wiki/Bloom_filter)などのデータ検索戦略を使用して、リダイレクトが存在するかどうかを大きなリダイレクトファイルやデータベースを読む前に効率的に確認します。 -前述の例では、生成されたブルームフィルターファイルをミドルウェアにインポートし、受信リクエストのパス名がブルームフィルターに存在するかどうかを確認します。 +前述の例を考えると、生成されたブルームフィルタ ファイルをMiddlewareにインポートし、受信リクエストのパス名がブルームフィルタに存在するかどうかを確認できます。 -存在する場合は、要求を[API Routes](https://nextjs.org/docs/canary/pages/building-your-application/routing/api-routes) [Route Handler](/docs/app/building-your-application/routing/route-handlers)に転送し、要求が実際に存在するかをチェックし、ユーザーを適切なURLにリダイレクトします。これにより、ミドルウェアに大量のリダイレクトファイルをインポートすることを避けることができ、すべての受信リクエストが遅くなるのを防ぎます。 +存在する場合、リクエストを[Route Handler](/docs/app/building-your-application/routing/route-handlers) [API Routes](https://nextjs.org/docs/canary/pages/building-your-application/routing/api-routes) にフォワードし、実際のファイルをチェックしてユーザーを適切なURLにリダイレクトします。これにより、大きなリダイレクトファイルをMiddlewareにインポートする必要がなくなり、受信リクエストの遅延を防ぎます。 - + -```tsx title="middleware.ts" switcher +```ts title="middleware.ts" switcher import { NextResponse, NextRequest } from 'next/server' import { ScalableBloomFilter } from 'bloom-filters' import GeneratedBloomFilter from './redirects/bloom-filter.json' @@ -473,23 +473,23 @@ type RedirectEntry = { permanent: boolean } -// 生成されたJSONファイルからブルームフィルターを初期化する +// JSONファイルから生成されたブルームフィルタを初期化 const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any) export async function middleware(request: NextRequest) { - // 受信リクエストのパスを取得する + // 受信リクエストのパスを取得 const pathname = request.nextUrl.pathname - // パスがブルームフィルターにあるかチェックする + // パスがブルームフィルタに存在するか確認 if (bloomFilter.has(pathname)) { - // パス名をRoute Handlerに転送する + // パス名をRoute Handlerにフォワード const api = new URL( `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`, request.nextUrl.origin ) try { - // Route Handlerからリダイレクトデータを取得する + // Route Handlerからリダイレクトデータを取得 const redirectData = await fetch(api) if (redirectData.ok) { @@ -497,10 +497,10 @@ export async function middleware(request: NextRequest) { await redirectData.json() if (redirectEntry) { - // ステータスコードを決定する + // ステータスコードを決定 const statusCode = redirectEntry.permanent ? 308 : 307 - // 宛先にリダイレクトする + // 目的地にリダイレクト return NextResponse.redirect(redirectEntry.destination, statusCode) } } @@ -509,7 +509,7 @@ export async function middleware(request: NextRequest) { } } - // リダイレクトが見つからなかった場合、そのままリクエストを続行します + // リダイレクトが見つからなかったのでリダイレクトせずにリクエストを続行 return NextResponse.next() } ``` @@ -522,33 +522,33 @@ import { NextResponse } from 'next/server' import { ScalableBloomFilter } from 'bloom-filters' import GeneratedBloomFilter from './redirects/bloom-filter.json' -// 生成されたJSONファイルからブルームフィルターを初期化する +// JSONファイルから生成されたブルームフィルタを初期化 const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter) export async function middleware(request) { - // 受信リクエストのパスを取得する + // 受信リクエストのパスを取得 const pathname = request.nextUrl.pathname - // パスがブルームフィルターにあるかチェックする + // パスがブルームフィルタに存在するか確認 if (bloomFilter.has(pathname)) { - // パス名をRoute Handlerに転送する + // パス名をRoute Handlerにフォワード const api = new URL( `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`, request.nextUrl.origin ) try { - // Route Handlerからリダイレクトデータを取得する + // Route Handlerからリダイレクトデータを取得 const redirectData = await fetch(api) if (redirectData.ok) { const redirectEntry = await redirectData.json() if (redirectEntry) { - // ステータスコードを決定する + // ステータスコードを決定 const statusCode = redirectEntry.permanent ? 308 : 307 - // 宛先にリダイレクトする + // 目的地にリダイレクト return NextResponse.redirect(redirectEntry.destination, statusCode) } } @@ -557,7 +557,7 @@ export async function middleware(request) { } } - // リダイレクトが見つからなかった場合、そのままリクエストを続行します + // リダイレクトが見つからなかったのでリダイレクトせずにリクエストを続行 return NextResponse.next() } ``` @@ -567,12 +567,12 @@ export async function middleware(request) { -その後、Route Handlerでは: +その後、Route Handler の中で: - + -```tsx title="app/redirects/route.ts" switcher +```ts title="app/api/redirects/route.ts" switcher import { NextRequest, NextResponse } from 'next/server' import redirects from '@/app/redirects/redirects.json' @@ -584,15 +584,15 @@ type RedirectEntry = { export function GET(request: NextRequest) { const pathname = request.nextUrl.searchParams.get('pathname') if (!pathname) { - return new Response('不正なリクエスト', { status: 400 }) + return new Response('Bad Request', { status: 400 }) } - // redirects.jsonファイルからリダイレクトエントリを取得する + // redirects.jsonファイルからリダイレクトエントリを取得 const redirect = (redirects as Record)[pathname] - // ブルームフィルターの誤検知に対応する + // ブルームフィルタの誤検知に対処 if (!redirect) { - return new Response('リダイレクトなし', { status: 400 }) + return new Response('No redirect', { status: 400 }) } // リダイレクトエントリを返す @@ -603,22 +603,22 @@ export function GET(request: NextRequest) { -```js title="app/redirects/route.js" switcher +```js title="app/api/redirects/route.js" switcher import { NextResponse } from 'next/server' import redirects from '@/app/redirects/redirects.json' export function GET(request) { const pathname = request.nextUrl.searchParams.get('pathname') if (!pathname) { - return new Response('不正なリクエスト', { status: 400 }) + return new Response('Bad Request', { status: 400 }) } - // redirects.jsonファイルからリダイレクトエントリを取得する + // redirects.jsonファイルからリダイレクトエントリを取得 const redirect = redirects[pathname] - // ブルームフィルターの誤検知に対応する + // ブルームフィルタの誤検知に対処 if (!redirect) { - return new Response('リダイレクトなし', { status: 400 }) + return new Response('No redirect', { status: 400 }) } // リダイレクトエントリを返す @@ -633,12 +633,12 @@ export function GET(request) { -その後、API Routeでは: +その後、API Route の中で: - + -```tsx title="pages/api/redirects.ts" switcher +```ts title="pages/api/redirects.ts" switcher import type { NextApiRequest, NextApiResponse } from 'next' import redirects from '@/app/redirects/redirects.json' @@ -650,15 +650,15 @@ type RedirectEntry = { export default function handler(req: NextApiRequest, res: NextApiResponse) { const pathname = req.query.pathname if (!pathname) { - return res.status(400).json({ message: '不正なリクエスト' }) + return res.status(400).json({ message: 'Bad Request' }) } - // redirects.jsonファイルからリダイレクトエントリを取得する + // redirects.jsonファイルからリダイレクトエントリを取得 const redirect = (redirects as Record)[pathname] - // ブルームフィルターの誤検知に対応する + // ブルームフィルタの誤検知に対処 if (!redirect) { - return res.status(400).json({ message: 'リダイレクトなし' }) + return res.status(400).json({ message: 'No redirect' }) } // リダイレクトエントリを返す @@ -675,15 +675,15 @@ import redirects from '@/app/redirects/redirects.json' export default function handler(req, res) { const pathname = req.query.pathname if (!pathname) { - return res.status(400).json({ message: '不正なリクエスト' }) + return res.status(400).json({ message: 'Bad Request' }) } - // redirects.jsonファイルからリダイレクトエントリを取得する + // redirects.jsonファイルからリダイレクトエントリを取得 const redirect = redirects[pathname] - // ブルームフィルターの誤検知に対応する + // ブルームフィルタの誤検知に対処 if (!redirect) { - return res.status(400).json({ message: 'リダイレクトなし' }) + return res.status(400).json({ message: 'No redirect' }) } // リダイレクトエントリを返す @@ -696,7 +696,7 @@ export default function handler(req, res) { -> **参考情報**: +> **Good to know:** > -> - ブルームフィルターを生成するために、[`bloom-filters`](https://www.npmjs.com/package/bloom-filters)のようなライブラリを使用できます; -> - 悪意のあるリクエストを防止するために、Route Handlerに対して行われるリクエストを検証する必要があります; +> - ブルームフィルタを生成するには、[`bloom-filters`](https://www.npmjs.com/package/bloom-filters)のようなライブラリを使用できます。 +> - 悪意のあるリクエストを防ぐために、Route Handler に送信されるリクエストを検証する必要があります。 diff --git a/docs/01-app/02-building-your-application/01-routing/15-internationalization.mdx b/docs/01-app/02-building-your-application/01-routing/15-internationalization.mdx index 1d78b7d..7bc298d 100644 --- a/docs/01-app/02-building-your-application/01-routing/15-internationalization.mdx +++ b/docs/01-app/02-building-your-application/01-routing/15-internationalization.mdx @@ -1,22 +1,22 @@ --- -title: '国際化' -description: '国際化されたルーティングと地域化されたコンテンツで複数言語のサポートを追加します' +title: 'Internationalization' +description: '国際化されたルーティングとローカライズされたコンテンツで複数言語のサポートを追加します。' --- -Next.jsは、コンテンツのルーティングとレンダリングを構成して、複数の言語をサポートすることを可能にします。異なるロケールにサイトを適応させることには、翻訳されたコンテンツ(地域化)と国際化されたルートが含まれます。 +Next.jsは、コンテンツのルーティングとレンダリングを構成して、複数の言語をサポートすることを可能にします。サイトをさまざまなロケールに適応させることには、翻訳されたコンテンツ(ローカライゼーション)と国際化されたルートが含まれます。 ## 用語 {#terminology} -- **Locale:** 一連の言語および形式設定の優先事項の識別子。通常、ユーザーの好みの言語とおそらく地理的な地域が含まれます。 +- **Locale(ロケール):** 言語とフォーマットの設定を示す識別子です。通常、ユーザーの希望言語や地理的な地域が含まれます。 - `en-US`: アメリカで話される英語 - `nl-NL`: オランダで話されるオランダ語 - - `nl`: 特定の地域が指定されていないオランダ語 + - `nl`: 特定の地域を示さないオランダ語 ## ルーティングの概要 {#routing-overview} -ブラウザでのユーザーの言語優先順位を使用して、どのロケールを使用するかを選択することをお勧めします。好みの言語を変更すると、アプリケーションに到着する`Accept-Language`ヘッダーが変更されます。 +ユーザーのブラウザでの言語設定を利用して、どのロケールを使用するかを選択することが推奨されます。希望言語を変更すると、アプリケーションの受け入れ言語ヘッダーが変更されます。 -例えば、以下のライブラリを使用して、受信する`Request`を見て、`Headers`、サポート予定のロケール、およびデフォルトのロケールに基づいて、どのロケールを選択するかを決定することができます。 +たとえば、以下のライブラリを使用して、受信した`Request`を基に、`Headers`、サポートするロケール、デフォルトのロケールに基づいて、選択するロケールを決定できます。 ```js title="middleware.js" import { match } from '@formatjs/intl-localematcher' @@ -30,18 +30,18 @@ let defaultLocale = 'en-US' match(languages, locales, defaultLocale) // -> 'en-US' ``` -ルーティングはサブパス (`/fr/products`) またはドメイン (`my-site.fr/products`) を使用して国際化できます。この情報を使用して、[Middleware](/docs/app/building-your-application/routing/middleware)内でロケールに基づいてユーザーをリダイレクトすることができます。 +ルーティングは、サブパス(`/fr/products`)またはドメイン(`my-site.fr/products`)によって国際化できます。この情報を使用して、[Middleware](/docs/app/building-your-application/routing/middleware)内でロケールに基づいてユーザーをリダイレクトできます。 ```js title="middleware.js" import { NextResponse } from "next/server"; let locales = ['en-US', 'nl-NL', 'nl'] -// 上記と同様にまたはライブラリを用いて、好みのロケールを取得する +// 上記と同様に、またはライブラリを使用して、お好みのロケールを取得します function getLocale(request) { ... } export function middleware(request) { - // パス名にサポートされているロケールがあるかどうかを確認 + // パス名にサポートされるロケールがあるかどうかを確認します const { pathname } = request.nextUrl const pathnameHasLocale = locales.some( (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}` @@ -49,41 +49,41 @@ export function middleware(request) { if (pathnameHasLocale) return - // ロケールがない場合はリダイレクト + // ロケールがない場合はリダイレクトします const locale = getLocale(request) request.nextUrl.pathname = `/${locale}${pathname}` - // 例: 受信リクエストが/productsの場合 - // 新しいURLは/en-US/productsになります + // 例: 受信リクエストが /products の場合 + // 新しいURLは /en-US/products になります return NextResponse.redirect(request.nextUrl) } export const config = { matcher: [ - // すべての内部パス (_next) をスキップ + // すべての内部パス(_next)をスキップします '/((?!_next).*)', - // 任意: root (/) URL でのみ実行 + // 任意: ルート(/) URLでのみ実行します // '/' ], } ``` -最後に、`app/`内のすべての特別なファイルが`app/[lang]`の下にネストされていることを確認してください。これにより、Next.js routerはルート内の異なるロケールを動的に処理し、`lang`パラメータをすべてのレイアウトとページに転送できるようになります。例えば: +最後に、`app/` 内のすべての特別なファイルが `app/[lang]` にネストされていることを確認します。これにより、Next.jsのルーターがルート内の異なるロケールを動的に処理し、`lang` パラメーターをすべてのレイアウトおよびページに転送できるようになります。例えば: ```jsx title="app/[lang]/page.js" -// 現在のロケールにアクセスできるようになります -// 例: /en-US/products -> `lang`は"en-US" +// 現在のロケールにアクセスできます +// 例: /en-US/products -> `lang` は "en-US" export default async function Page({ params: { lang } }) { return ... } ``` -root レイアウトも新しいフォルダー(例: `app/[lang]/layout.js`)にネストできます。 +root レイアウトも新しいフォルダーにネストすることができます(例: `app/[lang]/layout.js`)。 -## 地域化 {#localization} +## ローカライゼーション {#localization} -ユーザーの好みのロケールに基づいて表示されるコンテンツを変更する、つまり地域化はNext.jsに特有のものではありません。以下で説明するパターンは、任意のWebアプリケーションでも同様に機能します。 +ユーザーの優先ロケールに基づいて表示するコンテンツを変更すること、つまりローカライゼーションは、Next.jsに特有のものではありません。以下に説明するパターンは、どのWebアプリケーションでも同じように機能します。 -アプリケーション内で英語とオランダ語のコンテンツの両方をサポートしたいと仮定しましょう。いくつかのキーから地域化された文字列へのマッピングを提供するオブジェクトである2つの異なる「辞書」を管理するかもしれません。例えば: +アプリケーション内で英語とオランダ語の両方のコンテンツをサポートしたいとしましょう。異なる2つの「辞書」を維持することもできます。これは、いくつかのキーからローカライズされた文字列へのマッピングを提供するオブジェクトのことです。例えば: ```json title="dictionaries/en.json" { @@ -101,7 +101,7 @@ root レイアウトも新しいフォルダー(例: `app/[lang]/layout.js`) } ``` -次に、要求されたロケールの翻訳をロードするための`getDictionary`関数を作成できます: +次に、要求されたロケールの翻訳を読み込むための `getDictionary` 関数を作成できます: ```jsx title="app/[lang]/dictionaries.js" import 'server-only' @@ -114,22 +114,22 @@ const dictionaries = { export const getDictionary = async (locale) => dictionaries[locale]() ``` -現在選択されている言語に基づいて、レイアウトやページ内で辞書を取得できます。 +現在選択されている言語に基づいて、レイアウトまたはページ内で辞書を取得できます。 ```jsx title="app/[lang]/page.js" import { getDictionary } from './dictionaries' export default async function Page({ params: { lang } }) { const dict = await getDictionary(lang) // en - return // カートに追加 + return // Add to Cart } ``` -`app/`ディレクトリ内のすべてのレイアウトとページがデフォルトで[Server Components](/docs/app/building-your-application/rendering/server-components)であるため、翻訳ファイルのサイズがクライアント側JavaScriptバンドルサイズに影響を与えることを心配する必要はありません。このコードは**サーバー上でのみ実行され**、結果として得られるHTMLのみがブラウザーに送信されます。 +`app/` ディレクトリ内のすべてのレイアウトとページは、デフォルトで [Server Components](/docs/app/building-your-application/rendering/server-components)に設定されているため、クライアントサイドのJavaScriptバンドルサイズに翻訳ファイルのサイズが影響しないようにする必要はありません。このコードは**サーバー上でのみ実行されます**し、生成されたHTMLだけがブラウザに送信されます。 ## 静的生成 {#static-generation} -特定のロケールセットに対して静的ルートを生成するには、`generateStaticParams`をページやレイアウトで使用します。例えば、root レイアウトでこれをグローバルに行うことができます: +一連のロケールに対して静的ルートを生成するには、任意のページまたはレイアウトで `generateStaticParams` を使用できます。これは、root レイアウトでの例と同様に、グローバルでも可能です。 ```jsx title="app/[lang]/layout.js" export async function generateStaticParams() { @@ -147,7 +147,7 @@ export default function Root({ children, params }) { ## リソース {#resources} -- [Minimal i18n routing and translations](https://github.com/vercel/next.js/tree/canary/examples/app-dir-i18n-routing) +- [Minimal i18n routing and translations](https://github.com/vercel/next.js/tree/canary/examples/i18n-routing) - [`next-intl`](https://next-intl-docs.vercel.app/docs/next-13) - [`next-international`](https://github.com/QuiiBz/next-international) - [`next-i18n-router`](https://github.com/i18nexus/next-i18n-router) diff --git a/docs/01-app/02-building-your-application/02-data-fetching/01-fetching.mdx b/docs/01-app/02-building-your-application/02-data-fetching/01-fetching.mdx index 357b81e..0d20652 100644 --- a/docs/01-app/02-building-your-application/02-data-fetching/01-fetching.mdx +++ b/docs/01-app/02-building-your-application/02-data-fetching/01-fetching.mdx @@ -1,7 +1,7 @@ --- -title: 'データ取得とキャッシュ' -nav_title: 'データ取得とキャッシュ' -description: 'Next.jsでサーバーまたはクライアントでデータを取得するためのベストプラクティスを学びましょう。' +title: 'データフェッチとキャッシング' +nav_title: 'データフェッチとキャッシング' +description: 'Next.jsでサーバーまたはクライアントでデータを取得するためのベストプラクティスを学びます。' ---
@@ -13,17 +13,17 @@ description: 'Next.jsでサーバーまたはクライアントでデータを
-このガイドでは、Next.jsにおけるデータ取得とキャッシュの基本を実用的な例とベストプラクティスを示しながら説明します。 +このガイドは、Next.jsにおけるデータフェッチとキャッシングの基本について、実践的な例とベストプラクティスを提供しながら説明します。 -Next.jsにおけるデータ取得の最小限の例を以下に示します: +こちらはNext.jsでデータをフェッチする最小限の例です: ```tsx title="app/page.tsx" switcher export default async function Page() { - let data = await fetch('https://api.vercel.app/blog') - let posts = await data.json() + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() return (
    {posts.map((post) => ( @@ -39,8 +39,8 @@ export default async function Page() { ```jsx title="app/page.js" switcher export default async function Page() { - let data = await fetch('https://api.vercel.app/blog') - let posts = await data.json() + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() return (
      {posts.map((post) => ( @@ -54,9 +54,9 @@ export default async function Page() { -この例は、非同期のReact Server Componentで`fetch` APIを使用してサーバー側でデータを取得する基本的な方法を示しています。 +この例は、非同期のReact server componentで`fetch` APIを使用した基本的なサーバーサイドデータフェッチを示しています。 -## 参考 {#reference} +## 参考資料 {#reference} - [`fetch`](/docs/app/api-reference/functions/fetch) - React [`cache`](https://react.dev/reference/react/cache) @@ -64,17 +64,17 @@ export default async function Page() { ## 例 {#examples} -### サーバーで`fetch` APIを使用したデータ取得 {#fetching-data-on-the-server-with-the-fetch-api} +### サーバーで`fetch` APIを使用してデータを取得する {#fetching-data-on-the-server-with-the-fetch-api} -このコンポーネントはブログ投稿のリストを取得して表示します。`fetch`からのレスポンスはデフォルトではキャッシュされません。 +このコンポーネントは、ブログ投稿のリストをフェッチして表示します。`fetch`からのレスポンスはデフォルトではキャッシュされていません。 ```tsx title="app/page.tsx" switcher export default async function Page() { - let data = await fetch('https://api.vercel.app/blog') - let posts = await data.json() + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() return (
        {posts.map((post) => ( @@ -90,8 +90,8 @@ export default async function Page() { ```jsx title="app/page.js" switcher export default async function Page() { - let data = await fetch('https://api.vercel.app/blog') - let posts = await data.json() + const data = await fetch('https://api.vercel.app/blog') + const posts = await data.json() return (
          {posts.map((post) => ( @@ -105,19 +105,19 @@ export default async function Page() { -このルートの他の場所で[Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)を使用していない場合、`next build`中にこのページは静的ページとして事前レンダリングされます。その後、データは[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使用して更新できます。 +このルート内の他の場所で[Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)を使用していない場合、それは`next build`中にプレンダーされ、静的なページになります。データは[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使用して更新できます。 -ページの事前レンダリングを防ぐには、以下をファイルに追加します: +プレンダーリングを防ぐには、次の内容をファイルに追加できます: ```js export const dynamic = 'force-dynamic' ``` -ただし、多くの場合、`cookies`、`headers`、またはページpropsからの`searchParams`を読み取る関数を使用します。これによりページは自動的に動的にレンダリングされます。この場合、`force-dynamic`を明示的に使用する必要はありません。 +しかし、通常は`cookies`、`headers`のような関数やページpropsからの`searchParams`の読み取りを使うことがあり、ページは自動的に動的にレンダーされます。この場合、`force-dynamic`を明示的に使用する必要はありません。 -### ORMまたはデータベースを使用してサーバーでデータ取得 {#fetching-data-on-the-server-with-an-orm-or-database} +### ORMやデータベースを使用してサーバーでデータを取得する {#fetching-data-on-the-server-with-an-orm-or-database} -このコンポーネントはブログ投稿のリストを取得して表示します。データベースからのレスポンスはデフォルトではキャッシュされていませんが、[追加の設定](#caching-data-with-an-orm-or-database)によってキャッシュすることができます。 +このコンポーネントは、ブログポストのリストをフェッチして表示します。データベースからのレスポンスはデフォルトではキャッシュされていませんが、[追加のコンフィグレーション](#caching-data-with-an-orm-or-database)を行うことでキャッシュされることもあります。 @@ -126,7 +126,7 @@ export const dynamic = 'force-dynamic' import { db, posts } from '@/lib/db' export default async function Page() { - let allPosts = await db.select().from(posts) + const allPosts = await db.select().from(posts) return (
            {allPosts.map((post) => ( @@ -144,7 +144,7 @@ export default async function Page() { import { db, posts } from '@/lib/db' export default async function Page() { - let allPosts = await db.select().from(posts) + const allPosts = await db.select().from(posts) return (
              {allPosts.map((post) => ( @@ -158,21 +158,21 @@ export default async function Page() { -このルートの他の場所で[Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)を使用していない場合、`next build`中にこのページは静的ページとして事前レンダリングされます。その後、データは[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使用して更新できます。 +このルート内の他の場所で[Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)を使用していない場合、それは`next build`中にプレンダーされ、静的なページになります。データは[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使用して更新できます。 -ページの事前レンダリングを防ぐには、以下をファイルに追加します: +プレンダーリングを防ぐには、次の内容をファイルに追加できます: ```js export const dynamic = 'force-dynamic' ``` -ただし、多くの場合、`cookies`、`headers`、またはページpropsからの`searchParams`を読み取る関数を使用します。これによりページは自動的に動的にレンダリングされます。この場合、`force-dynamic`を明示的に使用する必要はありません。 +しかし、通常は`cookies`、`headers`のような関数やページpropsからの`searchParams`の読み取りを使うことがあり、ページは自動的に動的にレンダーされます。この場合、`force-dynamic`を明示的に使用する必要はありません。 ### クライアントでデータを取得する {#fetching-data-on-the-client} -最初にサーバーサイドでデータを取得することをお勧めします。 +まずはじめにサーバーサイドでデータをフェッチすることをお勧めします。 -ただし、クライアントサイドのデータ取得が理にかなう場合もあります。その際には、`useEffect`で手動で`fetch`を呼び出す(推奨されません)か、クライアント取得のためにコミュニティの人気のあるReactライブラリ(例:[SWR](https://swr.vercel.app/)や[React Query](https://tanstack.com/query/latest))を利用することができます。 +ただし、クライアントサイドでのデータフェッチが理にかなう場合もあります。このようなシナリオでは、手動で`fetch`を`useEffect`内で呼び出す(非推奨)、またはクライアントフェッチに人気のあるReactライブラリ([SWR](https://swr.vercel.app/)や[React Query](https://tanstack.com/query/latest)など)に頼ることができます。 @@ -187,8 +187,8 @@ export function Posts() { useEffect(() => { async function fetchPosts() { - let res = await fetch('https://api.vercel.app/blog') - let data = await res.json() + const res = await fetch('https://api.vercel.app/blog') + const data = await res.json() setPosts(data) } fetchPosts() @@ -219,8 +219,8 @@ export function Posts() { useEffect(() => { async function fetchPosts() { - let res = await fetch('https://api.vercel.app/blog') - let data = await res.json() + const res = await fetch('https://api.vercel.app/blog') + const data = await res.json() setPosts(data) } fetchPosts() @@ -241,9 +241,9 @@ export function Posts() { -### ORMまたはデータベースによるデータキャッシュ {#caching-data-with-an-orm-or-database} +### ORMやデータベースでデータをキャッシュする {#caching-data-with-an-orm-or-database} -`unstable_cache` APIを使用してレスポンスをキャッシュし、`next build`実行時にページを事前レンダリングすることができます。 +`unstable_cache` APIを使用してレスポンスをキャッシュし、`next build`を実行する際にページをプレンダーできるようにします。 @@ -304,22 +304,22 @@ export default async function Page() { -この例では、データベースクエリの結果を1時間(3600秒)キャッシュしています。また、キャッシュタグ`posts`を追加しており、後で[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)でインバリデートすることができます。 +この例では、データベースクエリの結果を1時間(3600秒)キャッシュします。また、キャッシュタグ`posts`が追加され、[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使って無効化できます。 -### 複数の関数でデータを再利用する {#reusing-data-across-multiple-functions} +### 複数の関数間でデータを再利用する {#reusing-data-across-multiple-functions} -Next.jsは、`generateMetadata`や`generateStaticParams`といったAPIを使用します。これらのAPIでは、`page`で取得したデータを使う必要があります。 +Next.jsは、`generateMetadata`や`generateStaticParams`などのAPIを使用し、`page`でフェッチしたデータを再利用する必要があります。 -もし`fetch`を使用している場合、リクエストに`cache: 'force-cache'`を追加するとリクエストが[メモ化](/docs/app/building-your-application/caching#request-memoization)されます。これにより、同じオプションで同じURLを安全に呼び出すことができ、リクエストは1回のみ行われます。 +`fetch`を使用している場合、リクエストは`cache: 'force-cache'`を追加することで[メモ化](/docs/app/building-your-application/caching#request-memoization)されます。これは、同じURLに同じオプションで安全に呼び出すとき、1つのリクエストだけが生成されることを意味します。 -> **Good to know:** +> **Good to know:** > -> - 以前のバージョンのNext.jsでは、`fetch`を使用するとデフォルトの`cache`値は`force-cache`でした。バージョン15では、デフォルトが`cache: no-store`に変更されました。 +> - 以前のバージョンのNext.jsでは、`fetch`を使用する際にデフォルトの`cache`の値が`force-cache`でした。バージョン15では、デフォルトが`cache: no-store`に変更されました。 -```tsx title="app/page.tsx" switcher +```tsx title="app/blog/[id]/page.tsx" switcher import { notFound } from 'next/navigation' interface Post { @@ -329,21 +329,21 @@ interface Post { } async function getPost(id: string) { - let res = await fetch(`https://api.vercel.app/blog/${id}`, { + const res = await fetch(`https://api.vercel.app/blog/${id}`, { cache: 'force-cache', }) - let post: Post = await res.json() + const post: Post = await res.json() if (!post) notFound() return post } export async function generateStaticParams() { - let posts = await fetch('https://api.vercel.app/blog', { + const posts = await fetch('https://api.vercel.app/blog', { cache: 'force-cache', }).then((res) => res.json()) return posts.map((post: Post) => ({ - id: post.id, + id: String(post.id), })) } @@ -352,7 +352,8 @@ export async function generateMetadata({ }: { params: Promise<{ id: string }> }) { - let post = await getPost(params.id) + const { id } = await params + const post = await getPost(id) return { title: post.title, @@ -364,7 +365,8 @@ export default async function Page({ }: { params: Promise<{ id: string }> }) { - let post = await getPost(params.id) + const { id } = await params + const post = await getPost(id) return (
              @@ -378,28 +380,29 @@ export default async function Page({ -```jsx title="app/page.js" switcher +```jsx title="app/blog/[id]/page.js" switcher import { notFound } from 'next/navigation' async function getPost(id) { - let res = await fetch(`https://api.vercel.app/blog/${id}`) - let post = await res.json() + const res = await fetch(`https://api.vercel.app/blog/${id}`) + const post = await res.json() if (!post) notFound() return post } export async function generateStaticParams() { - let posts = await fetch('https://api.vercel.app/blog').then((res) => + const posts = await fetch('https://api.vercel.app/blog').then((res) => res.json() ) return posts.map((post) => ({ - id: post.id, + id: String(post.id), })) } export async function generateMetadata({ params }) { - let post = await getPost(params.id) + const { id } = await params + const post = await getPost(id) return { title: post.title, @@ -407,7 +410,8 @@ export async function generateMetadata({ params }) { } export default async function Page({ params }) { - let post = await getPost(params.id) + const { id } = await params + const post = await getPost(id) return (
              @@ -421,11 +425,11 @@ export default async function Page({ params }) { -もし`fetch`を使用していない場合、ORMやデータベースを直接使用する代わりに、Reactの`cache`関数でデータ取得をラップできます。これにより重複が排除され、クエリは1回のみ行われます。 +`fetch`を使用していない場合、ORMやデータベースを直接使用する場合、データフェッチをReactの`cache`関数でラップすることができます。これにより、データフェッチが重複しなくなり、1つのクエリだけが作成されます。 ```jsx import { cache } from 'react' -import { db, posts, eq } from '@/lib/db' // Drizzle ORMでの例 +import { db, posts, eq } from '@/lib/db' // Drizzle ORMを用いた例 import { notFound } from 'next/navigation' export const getPost = cache(async (id) => { @@ -440,49 +444,50 @@ export const getPost = cache(async (id) => { ### キャッシュされたデータの再検証 {#revalidating-cached-data} -[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を使用したキャッシュされたデータの再検証について詳しく学びましょう。 +キャッシュされたデータの再検証についての詳細は、[Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)を参照してください。 ## パターン {#patterns} -### 並列および逐次データ取得 {#parallel-and-sequential-data-fetching} +### 並列と逐次データフェッチ {#parallel-and-sequential-data-fetching} -コンポーネント内でデータを取得する際には、並列および逐次の2つのデータ取得パターンを意識する必要があります。 +コンポーネント内部でデータをフェッチする場合、並列と逐次の2つのデータフェッチパターンを意識する必要があります。 順次および並列データ取得 -- **逐次**: コンポーネントtree内でリクエストが互いに依存している。この結果、読み込み時間が長くなることがあります -- **並列**: route内のリクエストが積極的に開始され、データは同時に読み込まれます。これにより、データの読み込みにかかる全体の時間が短縮されます +- **逐次**:コンポーネントツリー内のリクエストが互いに依存している状態。この場合、読み込み時間が長くなる可能性があります。 +- **並列**:ルート内のリクエストが意欲的に開始され、データが同時に読み込まれる状態。この方法では、データ読み込みにかかる総時間を短縮できます。 -#### 逐次データ取得 {#sequential-data-fetching} +#### 逐次データフェッチ {#sequential-data-fetching} -ネストされたコンポーネントがあり、各コンポーネントが独自にデータを取得する場合、これらのデータリクエストが[メモ化](/docs/app/building-your-application/caching#request-memoization)されていない場合、データ取得は逐次的に行われます。 +ネストされたコンポーネントがあり、それぞれが独自のデータをフェッチする場合、これらのデータリクエストが[メモ化されて](/docs/app/building-your-application/caching#request-memoization)いない限り、データフェッチは逐次的に行われます。 -このパターンが望ましい場合もあります。たとえば、`Playlists`コンポーネントは`Artist`コンポーネントがデータ取得を完了してからデータを取得し始めます。`Playlists`は`artistID` propに依存しているためです: +このパターンを使用したいケースもあるかもしれません。なぜなら、あるフェッチが他のフェッチの結果に依存しているからです。例えば、`Playlists`コンポーネントは`artistID` propに依存しているため、`Artist`コンポーネントがデータフェッチを完了してから`Playlists`のデータフェッチを開始します。 ```tsx title="app/artist/[username]/page.tsx" switcher export default async function Page({ - params: { username }, + params, }: { params: Promise<{ username: string }> }) { - // アーティスト情報の取得 + const { username } = await params + // アーティスト情報を取得 const artist = await getArtist(username) return ( <>

              {artist.name}

              - {/* Playlistsコンポーネントの読み込み中にフォールバックUIを表示 */} + {/* Playlistsコンポーネントが読み込まれるまでフォールバックUIを表示 */} Loading...}> - {/* アーティストIDをPlaylistsコンポーネントに渡す */} + {/* PlaylistsコンポーネントにアーティストIDを渡す */} @@ -490,7 +495,7 @@ export default async function Page({ } async function Playlists({ artistID }: { artistID: string }) { - // アーティストIDを使用してプレイリストを取得 + // アーティストIDを使用してプレイリストをフェッチ const playlists = await getArtistPlaylists(artistID) return ( @@ -507,16 +512,17 @@ async function Playlists({ artistID }: { artistID: string }) { ```jsx title="app/artist/[username]/page.js" switcher -export default async function Page({ params: { username } }) { - // アーティスト情報の取得 +export default async function Page({ params }) { + const { username } = await params + // アーティスト情報を取得 const artist = await getArtist(username) return ( <>

              {artist.name}

              - {/* Playlistsコンポーネントの読み込み中にフォールバックUIを表示 */} + {/* Playlistsコンポーネントが読み込まれるまでフォールバックUIを表示 */} Loading...}> - {/* アーティストIDをPlaylistsコンポーネントに渡す */} + {/* PlaylistsコンポーネントにアーティストIDを渡す */} @@ -524,7 +530,7 @@ export default async function Page({ params: { username } }) { } async function Playlists({ artistID }) { - // アーティストIDを使用してプレイリストを取得 + // アーティストIDを使用してプレイリストをフェッチ const playlists = await getArtistPlaylists(artistID) return ( @@ -540,19 +546,19 @@ async function Playlists({ artistID }) {
              -[`loading.js`](/docs/app/building-your-application/routing/loading-ui-and-streaming)(routeセグメント用)または[React ``](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)(ネストされたコンポーネント用)を使用して、即時の読み込み状態を表示することができます。 +[`loading.js`](/docs/app/building-your-application/routing/loading-ui-and-streaming)(ルートセグメント用)や[React ``](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)(ネストされたコンポーネント用)を使って、即座にローディング状態を表示し、Reactが結果をストリームに流す間、インタラクションできるようにします。 -これにより、データリクエストによってroute全体がブロックされるのを防ぎ、ユーザーは準備ができているページの部分と対話できるようになります。 +これによりデータリクエストによってルート全体がブロックされるのを防ぎ、ユーザーは準備が整ったページの一部とインタラクションできます。 -#### 並列データ取得 {#parallel-data-fetching} +#### 並列データフェッチ {#parallel-data-fetching} -デフォルトで、レイアウトとページセグメントは並列にレンダリングされます。これにより、リクエストは並行して開始されます。 +デフォルトでは、レイアウトとページセグメントは並列でレンダーされます。これによりリクエストは並列に開始されます。 -しかし、`async`/`await`の特性上、同じセグメントまたはコンポーネント内で待たれるリクエストはそれ以下のリクエストをブロックします。 +しかし、`async`/`await`の性質上、同じセグメントやコンポーネント内で待たれているリクエストは、それ以下のリクエストをブロックします。 -データを並列に取得するには、データを使用するコンポーネントの外側でリクエストを定義して積極的に開始します。これにより、両方のリクエストが並行して開始され時間を節約しますが、両方のプロミスが解決されるまでユーザーはレンダリングされた結果を目にすることはありません。 +データを並列でフェッチするには、データを使用するコンポーネント外でリクエストを意欲的に開始することができます。こうすることで両方のリクエストを並列に開始できますが、両方のプロミスが解決されるまでレンダー結果は表示されません。 -下の例では、`getArtist`と`getAlbums`関数が`Page`コンポーネントの外側に定義され、コンポーネント内で`Promise.all`を使用して開始されます: +以下の例では、`getArtist`と`getAlbums`関数が`Page`コンポーネントの外で定義され、`Promise.all`を使用してコンポーネント内で開始されています: @@ -571,14 +577,15 @@ async function getAlbums(username: string) { } export default async function Page({ - params: { username }, + params, }: { params: Promise<{ username: string }> }) { + const { username } = await params const artistData = getArtist(username) const albumsData = getAlbums(username) - // 両方のリクエストを並行して開始する + // 両方のリクエストを並列に開始 const [artist, albums] = await Promise.all([artistData, albumsData]) return ( @@ -606,11 +613,12 @@ async function getAlbums(username) { return res.json() } -export default async function Page({ params: { username } }) { +export default async function Page({ params }) { + const { username } = await params const artistData = getArtist(username) const albumsData = getAlbums(username) - // 両方のリクエストを並行して開始する + // 両方のリクエストを並列に開始 const [artist, albums] = await Promise.all([artistData, albumsData]) return ( @@ -625,13 +633,13 @@ export default async function Page({ params: { username } }) { -さらに、[Suspense Boundary](/docs/app/building-your-application/routing/loading-ui-and-streaming)を追加することで、レンダリング作業を分割し可能な限り早く部分的な結果を表示することができます。 +さらに、[Suspense Boundary](/docs/app/building-your-application/routing/loading-ui-and-streaming)を追加して、レンダー作業を分割し、可能な限り早く部分的に結果を表示することができます。 -### データの事前ロード {#preloading-data} +### データのプレロード {#preloading-data} -ウォーターフォールを防ぐもう1つの方法は、ユーティリティ関数を作成し、ブロッキングリクエストの上で積極的に呼び出す*preload*パターンを使用することです。たとえば、`checkIsAvailable()`は``のレンダリングをブロックするので、`preload()`をその前に呼び出して``のデータ依存関係を積極的に開始できます。``がレンダリングされるとき、そのデータはすでに取得されています。 +ウォーターフォールを防ぐもう一つの方法は、ブロッキングリクエストの上にユーティリティ関数を作成して意欲的に呼び出すことです。たとえば、`checkIsAvailable()`は`をレンダーするのをブロックするので、`preload()`をその前に呼び出して`のデータ依存性を意欲的に開始します。`Item`がレンダーされる時点で、そのデータはすでにフェッチされていることになります。 -なお、`preload`関数は`checkIsAvailable()`の実行をブロックしません。 +ただし、`preload`関数は`checkIsAvailable()`の実行をブロックすることはありません。 @@ -640,7 +648,7 @@ export default async function Page({ params: { username } }) { import { getItem } from '@/utils/get-item' export const preload = (id: string) => { - // voidは指定された式を評価して未定義を返します。 + // voidは指定された式を評価し、undefinedを返します // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void void getItem(id) } @@ -657,7 +665,7 @@ export default async function Item({ id }: { id: string }) { import { getItem } from '@/utils/get-item' export const preload = (id) => { - // voidは指定された式を評価して未定義を返します。 + // voidは指定された式を評価し、undefinedを返します // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void void getItem(id) } @@ -677,13 +685,14 @@ export default async function Item({ id }) { import Item, { preload, checkIsAvailable } from '@/components/Item' export default async function Page({ - params: { id }, + params, }: { params: Promise<{ id: string }> }) { - // アイテムデータの読み込みを開始 + const { id } = await params + // ユーティリティ関数を使用してデータのプレロードを開始 preload(id) - // 他の非同期タスクを実行 + // ほかの非同期タスクを実行 const isAvailable = await checkIsAvailable() return isAvailable ? : null @@ -696,10 +705,11 @@ export default async function Page({ ```jsx title="app/item/[id]/page.js" switcher import Item, { preload, checkIsAvailable } from '@/components/Item' -export default async function Page({ params: { id } }) { - // アイテムデータの読み込みを開始 +export default async function Page({ params }) { + const { id } = await params + // ユーティリティ関数を使用してデータのプレロードを開始 preload(id) - // 他の非同期タスクを実行 + // ほかの非同期タスクを実行 const isAvailable = await checkIsAvailable() return isAvailable ? : null @@ -709,11 +719,11 @@ export default async function Page({ params: { id } }) { -> **Good to know:** "preload"関数はAPIではなくパターンであるため、任意の名前を持つことができます。 +> **Good to know:** 「preload」関数はAPIではなくパターンであるため、任意の名前を持つことができます。 -#### Reactの`cache`と`server-only`を使用したPreloadパターン {#using-react-cache-and-server-only-with-the-preload-pattern} +#### React `cache`と`server-only`を使用したプレロード・パターン {#using-react-cache-and-server-only-with-the-preload-pattern} -`cache`関数、preloadパターン、`server-only`パッケージを組み合わせて、アプリ全体で使用可能なデータ取得ユーティリティを作成することができます。 +`cache`関数、プレロード・パターン、および`server-only`パッケージを組み合わせて、アプリ全体で利用可能なデータフェッチユーティリティを作成できます。 @@ -750,17 +760,17 @@ export const getItem = cache(async (id) => { -このアプローチを使用すると、データを積極的に取得し、レスポンスをキャッシュし、このデータ取得が[サーバー上でのみ行われることを保証](/docs/app/building-your-application/rendering/composition-patterns#keeping-server-only-code-out-of-the-client-environment)することができます。 +このアプローチにより、データを意欲的にフェッチし、レスポンスをキャッシュし、このデータフェッチ[がサーバー上だけで発生する](/docs/app/building-your-application/rendering/composition-patterns#keeping-server-only-code-out-of-the-client-environment)ことを保証できます。 -`utils/get-item`エクスポートは、レイアウト、ページ、または他のコンポーネントによって使用され、アイテムのデータ取得のタイミングを制御することができます。 +`utils/get-item`のエクスポートは、Layouts、Pages、または他のコンポーネントで使用することができ、それらにデータフェッチのタイミングをコントロールできるようにします。 -> **Good to know:** +> **Good to know:** > -> - サーバー側のデータ取得関数がクライアントで使用されないことを確認するために、[`server-only`パッケージ](/docs/app/building-your-application/rendering/composition-patterns#keeping-server-only-code-out-of-the-client-environment)を使用することを推奨します。 +> - サーバー上のデータフェッチ関数がクライアントで使用されないことを確認するために[`server-only`パッケージ](/docs/app/building-your-application/rendering/composition-patterns#keeping-server-only-code-out-of-the-client-environment)の使用をお勧めします。 -### クライアントへの機密データの露出を防ぐ {#preventing-sensitive-data-from-being-exposed-to-the-client} +### クライアントに機密データが露出しないようにする {#preventing-sensitive-data-from-being-exposed-to-the-client} -オブジェクトインスタンスや機密値全体がクライアントに渡されないようにするために、Reactの汚染API、[`taintObjectReference`](https://react.dev/reference/react/experimental_taintObjectReference)および[`taintUniqueValue`](https://react.dev/reference/react/experimental_taintUniqueValue)を使用することをお勧めします。 +オブジェクトインスタンス全体や機密値がクライアントに渡されないようにするために、Reactの汚染(taint)APIである[`taintObjectReference`](https://react.dev/reference/react/experimental_taintObjectReference)と[`taintUniqueValue`](https://react.dev/reference/react/experimental_taintUniqueValue)を使用することをお勧めします。 アプリケーションで汚染を有効にするには、Next.js Configの`experimental.taint`オプションを`true`に設定します: @@ -772,7 +782,7 @@ module.exports = { } ``` -次に、`experimental_taintObjectReference`または`experimental_taintUniqueValue`関数に渡したいオブジェクトまたは値を渡します: +次に、`experimental_taintObjectReference`または`experimental_taintUniqueValue` 関数に汚染したいオブジェクトや値を渡します: @@ -787,11 +797,11 @@ import { export async function getUserData() { const data = await queryDataFromDB() experimental_taintObjectReference( - 'Do not pass the whole user object to the client', + 'オブジェクト全体をクライアントに渡さない', data ) experimental_taintUniqueValue( - "Do not pass the user's address to the client", + 'ユーザーの住所をクライアントに渡さない', data, data.address ) @@ -812,11 +822,11 @@ import { export async function getUserData() { const data = await queryDataFromDB() experimental_taintObjectReference( - 'Do not pass the whole user object to the client', + 'オブジェクト全体をクライアントに渡さない', data ) experimental_taintUniqueValue( - "Do not pass the user's address to the client", + 'ユーザーの住所をクライアントに渡さない', data, data.address ) @@ -837,8 +847,8 @@ export async function Page() { const userData = getUserData() return ( ) } @@ -854,8 +864,8 @@ export async function Page() { const userData = await getUserData() return ( ) } diff --git a/docs/01-app/02-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx b/docs/01-app/02-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx index a998744..9d16115 100644 --- a/docs/01-app/02-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx +++ b/docs/01-app/02-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx @@ -1,24 +1,24 @@ --- -title: 'Server Actions とデータ変更' -nav_title: 'Server Actions とデータ変更' -description: 'Next.jsを用いたフォーム送信とデータ変更の処理方法を学びましょう。' +title: 'Server ActionsとMutation' +nav_title: 'Server ActionsとMutation' +description: 'Next.js でのフォーム送信とデータのMutationの処理方法を学びましょう。' related: - description: 'Next.jsにおけるServer Actionsの設定方法を学びましょう' + description: 'Next.js でServer Actions を設定する方法を学ぶ' links: - app/api-reference/config/next-config-js/serverActions --- -[Server Actions](https://react.dev/reference/rsc/server-actions) は **非同期関数** であり、サーバー上で実行されます。これらはServerおよびClient Componentsの中で、Next.jsアプリケーションでのフォーム送信とデータの変更を管理するために呼び出すことができます。 +[Server Actions](https://react.dev/reference/rsc/server-actions) は、サーバーで実行される**非同期関数**です。これらは Next.js アプリケーション内でフォーム送信やデータのMutationを処理するために、Server Component と Client Component で呼び出すことができます。 -> **🎥 視聴:** Server Actions を用いたデータ変更について詳しく学びましょう → [YouTube (10 分)](https://youtu.be/dDpZfOQBMaU?si=cJZHlUu_jFhCzHUg)。 +> **🎥 視聴**: Server Actions による Mutation についてさらに学ぶ → [YouTube (10 分)](https://youtu.be/dDpZfOQBMaU?si=cJZHlUu_jFhCzHUg). ## 規約 {#convention} -Server Action はReactの[`"use server"`](https://react.dev/reference/react/use-server)ディレクティブを用いて定義できます。非同期関数の上部にディレクティブを置いて、関数をServer Actionとしてマークするか、別ファイルの上部にディレクティブを置いて、そのファイルのすべてのエクスポートをServer Actionとしてマークすることができます。 +Server ActionはReactの[`"use server"`](https://react.dev/reference/react/use-server) のディレクティブで定義できます。このディレクティブを `async` 関数の先頭に置くことで、その関数をServer Actionとしてマークしたり、別ファイルの先頭に置くことで、そのファイルの全てのエクスポートをServer Actionとしてマークできます。 ### Server Components {#server-components} -Server Components は、インライン関数レベルまたはモジュールレベルで`"use server"`ディレクティブを使用できます。Server Actionをインラインで使うには、関数本体の上部に`"use server"`を追加してください: +Server Component は、インラインの関数レベルまたはモジュールレベルの `"use server"` ディレクティブを使用できます。Server Action をインライン化するには、関数本体の先頭に `"use server"` を追加します: @@ -28,7 +28,7 @@ export default function Page() { // Server Action async function create() { 'use server' - // データを変更する + // データをMutationする } return '...' @@ -43,7 +43,7 @@ export default function Page() { // Server Action async function create() { 'use server' - // データを変更する + // データをMutationする } return '...' @@ -55,7 +55,7 @@ export default function Page() { ### Client Components {#client-components} -Client ComponentでServer Actionを呼び出すには、新しいファイルを作成して、その上部に「use server」ディレクティブを追加します。そのファイル内でエクスポートされるすべての関数が、ClientとServer Componentの両方で再利用可能なServer Actionとしてマークされます。 +Client ComponentでServer Actionを呼び出すには、新しいファイルを作成し、そのファイルの先頭に `"use server"` ディレクティブを追加します。ファイル内のすべてのエクスポートされた関数は、Client ComponentとServer Componentの両方で再利用可能なServer Actionとしてマークされます: @@ -107,7 +107,7 @@ export function Button() { -### アクションを props として渡す {#passing-actions-as-props} +### アクションをpropsとして渡す {#passing-actions-as-props} Server Action を Client Component に props として渡すこともできます: @@ -144,29 +144,32 @@ export default function ClientComponent({ updateItemAction }) { -通常、Next.js TypeScriptプラグインは `client-component.tsx` の `updateItemAction` を警告しますが、これは一般にクライアント側とサーバー側の境界を越えてシリアル化できない関数だからです。しかし、`action` として名前付けされた props または `Action` で終了する props は Server Actions を受け取ることが想定されています。これはあくまでヒューリスティックであり、TypeScriptプラグインは実際に Server Action を受け取っているか、通常の関数を受け取っているかはわかりません。ランタイム型チェックにより、間違って関数をClient Componentに渡さないようにします。 - -## 行動 {#behavior} - -- Server actionは、[`
              `要素](#forms) の `action` 属性を使用して呼び出すことができます: - - Server Componentsはデフォルトでプログレッシブエンハンスメントをサポートしており、JavaScriptがまだ読み込まれていない場合や無効になっている場合でもフォームが送信されます。 - - Client Componentsでは、JavaScriptがまだ読み込まれていない場合、Server Actions を呼び出すフォームは送信をキューに入れ、クライアントのハイドレーションを優先します。 - - ハイドレーション後、ブラウザはフォーム送信時にリフレッシュしません。 -- Server Actionsは``に限定されず、イベントハンドラ、`useEffect`、サードパーティのライブラリ、`