プリフェッチング
プリフェッチングにより、アプリケーション内での異なるルート間のナビゲーションが瞬時に感じられます。Next.jsはデフォルトでアプリケーションコードで使用されるリンクに基づいて、インテリジェントにプリフェッチを試みます。
このガイドではプリフェッチングの仕組みを説明し、一般的な実装パターンを示します:
プリフェッチングはどのように機能しますか?
ルート間をナビゲートするとき、ブラウザはHTMLやJavaScriptファイルなどのページのアセットをリクエストします。プリフェッチングとは、新しいルートにナビゲートする前に、これらのリソースを事前に取得するプロセスです。
Next.jsはルートに基づいて、アプリケーションをより小さなJavaScriptチャンクに自動分割します。従来のSPAのようにすべてのコードを最初に読み込む代わりに、現在のルートに必要なコードのみが読み込まれます。これにより初期読み込み時間が短縮され、アプリケーションの他の部分がバックグラウンドで読み込まれます。リンクをクリックするまでに、新しいルートのリソースはすでにブラウザキャッシュに読み込まれています。
新しいページにナビゲートするとき、完全なページリロードやブラウザのローディングスピナーはありません。代わりに、Next.jsはクライアントサイドトランジションを実行し、ページナビゲーションを瞬時に感じさせます。
静的ルートと動的ルートのプリフェッチング
| 静的ページ | 動的ページ | |
|---|---|---|
| プリフェッチ対象 | はい、完全ルート | いいえ、loading.jsを除く |
| クライアントキャッシュTTL | 5分(デフォルト) | オフ、有効化の場合を除く |
| クリック時のサーバーラウンドトリップ | いいえ | はい、shell後にストリーミング |
補足: 初期ナビゲーション中、ブラウザはHTML、JavaScript、およびReact Server Components(RSC)ペイロードを取得します。その後のナビゲーションでは、ブラウザはServer ComponentsのRSCペイロードとClient ComponentsのJSバンドルを取得します。
自動プリフェッチ
import Link from 'next/link'
export default function NavLink() {
return <Link href="/about">About</Link>
}| コンテキスト | プリフェッチペイロード | クライアントキャッシュTTL |
|---|---|---|
loading.jsなし | ページ全体 | アプリリロードまで |
loading.jsあり | レイアウトから最初のローディング境界まで | 30秒(設定可能) |
自動プリフェッチは本番環境でのみ実行されます。prefetch={false}で無効にするか、プリフェッチ無効のラッパーを使用してください。
手動プリフェッチ
手動プリフェッチを行うには、next/navigationからuseRouterフックをインポートし、ビューポート外のルートをウォームアップするか、分析、ホバー、スクロールなどに応じてrouter.prefetch()を呼び出します。
'use client'
import { useRouter } from 'next/navigation'
import { CustomLink } from '@components/link'
export function PricingCard() {
const router = useRouter()
return (
<div onMouseEnter={() => router.prefetch('/pricing')}>
{/* other UI elements */}
<CustomLink href="/pricing">View Pricing</CustomLink>
</div>
)
}コンポーネント読み込み時にURLをプリフェッチする意図がある場合は、リンクの拡張またはリジェクション[例]を参照してください。
ホバートリガープリフェッチ
注意:
Linkを拡張すると、プリフェッチング、キャッシュ無効化、およびアクセシビリティの懸念を維持する責任が生じます。デフォルトが不十分な場合のみ進めてください。
Next.jsはデフォルトで正しいプリフェッチングを試みますが、パワーユーザーはエジェクトしてニーズに応じて修正できます。パフォーマンスとリソース消費のバランスを制御できます。
たとえば、ビューポート進入時(デフォルト動作)ではなく、ホバー時にのみプリフェッチをトリガーする必要がある場合があります:
'use client'
import Link from 'next/link'
import { useState } from 'react'
export function HoverPrefetchLink({
href,
children,
}: {
href: string
children: React.ReactNode
}) {
const [active, setActive] = useState(false)
return (
<Link
href={href}
prefetch={active ? null : false}
onMouseEnter={() => setActive(true)}
>
{children}
</Link>
)
}prefetch={null}ではユーザーが意図を示すと、デフォルト(静的)プリフェッチが復元されます。
リンクの拡張またはエジェクト
<Link>コンポーネントを拡張して、独自のカスタムプリフェッチング戦略を作成できます。たとえば、ユーザーのカーソル方向を予測してリンクをプリフェッチするForesightJSライブラリを使用します。
別の方法として、useRouterを使用してネイティブ<Link>動作の一部を再作成できます。ただし、これはプリフェッチングとキャッシュ無効化を維持する責任が生じることに注意してください。
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
function ManualPrefetchLink({
href,
children,
}: {
href: string
children: React.ReactNode
}) {
const router = useRouter()
useEffect(() => {
let cancelled = false
const poll = () => {
if (!cancelled) router.prefetch(href, { onInvalidate: poll })
}
poll()
return () => {
cancelled = true
}
}, [href, router])
return (
href={href}
onClick={(event) => {
event.preventDefault()
router.push(href)
}}
>
{children}
</a>
)
}onInvalidateはNext.jsがキャッシュされたデータが古いと疑う場合に呼び出され、プリフェッチを更新できます。
補足:
aタグを使用するとデスティネーションルートへの完全なページナビゲーションが発生します。onClickを使用して完全なページナビゲーションを防止してから、router.pushを呼び出してデスティネーションにナビゲートできます。
プリフェッチ無効
リソース消費をより細かく制御するため、特定のルートのプリフェッチングを完全に無効にできます。
'use client'
import Link, { LinkProps } from 'next/link'
function NoPrefetchLink({
prefetch,
...rest
}: LinkProps & { children: React.ReactNode }) {
return <Link {...rest} prefetch={false} />
}たとえば、アプリケーション全体で<Link>の一貫した使用を維持したいが、フッターのリンクはビューポート進入時にプリフェッチされる必要がない場合があります。
プリフェッチング最適化
補足: レイアウト重複排除とプリフェッチスケジューリングは今後の最適化の一部です。現在
experimental.clientSegmentCacheフラグ経由でNext.js canaryで利用可能です。
クライアントキャッシュ
Next.jsはプリフェッチされたReact Server Componentペイロードをメモリに格納し、ルートセグメントをキーとしています。兄弟ルート間をナビゲートするとき(例:/dashboard/settings → /dashboard/analytics)、親レイアウトを再利用し、更新されたリーフページのみを取得します。これはネットワークトラフィックを削減し、ナビゲーション速度を向上させます。
プリフェッチスケジューリング
Next.jsは小さいタスクキューを維持し、次の順序でプリフェッチします:
- ビューポート内のリンク
- ユーザーの意図を示すリンク(ホバーまたはタッチ)
- 新しいリンクが古いものを置き換える
- 画面外にスクロールされたリンクは破棄される
スケジューラは可能性の高いナビゲーションを優先しながら、未使用のダウンロードを最小限に抑えます。
部分的プリレンダリング(PPR)
PPRが有効な場合、ページは静的シェルとストリーミング動的セクションに分割されます:
- プリフェッチ可能なシェルは直ちにストリーミング
- 動的データは準備完了時にストリーミング
- データ無効化(
revalidateTag、revalidatePath)は関連プリフェッチをサイレントに更新
トラブルシューティング
プリフェッチング中に不要な副作用がトリガーされる
レイアウトまたはページが純粋でなく、副作用がある場合(例:分析トラッキング)、ユーザーがページを訪問するときではなく、ルートがプリフェッチされるときにトリガーされる可能性があります。
これを回避するには、副作用をuseEffectフックまたはClient ComponentからトリガーされるServer Actionに移動してください。
変更前:
import { trackPageView } from '@/lib/analytics'
export default function Layout({ children }: { children: React.ReactNode }) {
// This runs during prefetch
trackPageView()
return <div>{children}</div>
}変更後:
'use client'
import { useEffect } from 'react'
import { trackPageView } from '@/lib/analytics'
export function AnalyticsTracker() {
useEffect(() => {
trackPageView()
}, [])
return null
}import { AnalyticsTracker } from '@/app/ui/analytics-tracker'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<AnalyticsTracker />
{children}
</div>
)
}過剰なプリフェッチを防止する
Next.jsは<Link>コンポーネントを使用する場合、ビューポート内のリンクを自動的にプリフェッチします。
不要なリソース使用を避けるため、このことを防ぎたい場合があります。たとえば、大量のリンクを描画するとき(例:無限スクロールテーブル)などです。
<Link>コンポーネントのprefetchプロップをfalseに設定することでプリフェッチングを無効にできます。
<Link prefetch={false} href={`/blog/${post.id}`}>
{post.title}
</Link>ただし、これは静的ルートがクリック時にのみ取得され、動的ルートがナビゲート前にサーバーがレンダリングするのを待つことを意味します。
プリフェッチを完全に無効にせずリソース使用を削減するには、ユーザーがリンクをホバーするまでプリフェッチングを遅延させます。これはユーザーが訪問する可能性の高いリンクのみを対象とします。
'use client'
import Link from 'next/link'
import { useState } from 'react'
export function HoverPrefetchLink({
href,
children,
}: {
href: string
children: React.ReactNode
}) {
const [active, setActive] = useState(false)
return (
<Link
href={href}
prefetch={active ? null : false}
onMouseEnter={() => setActive(true)}
>
{children}
</Link>
)
}