跳至內容

連結與導航

在 Next.js 中,有四種方式可以在路由之間導航:

本頁面將介紹如何使用這些選項,並深入探討導航的運作方式。

<Link> 是一個內建元件,它擴展了 HTML <a> 標籤,以提供路由之間的預先提取和客戶端導航。它是 Next.js 中路由之間導航的主要和推薦方式。

您可以從 next/link 導入它,並將 href 屬性傳遞給元件來使用它

app/page.tsx
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

您可以將其他可選屬性傳遞給 <Link>。請參閱 API 參考 以了解更多資訊。

useRouter() hook

useRouter hook 允許您從客戶端元件以程式化的方式變更路由。

app/page.tsx
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

如需 useRouter 方法的完整列表,請參閱 API 參考

建議: 除非您有使用 useRouter 的特定需求,否則請使用 <Link> 元件在路由之間導航。

redirect 函式

對於伺服器元件,請改用 redirect 函式。

app/team/[id]/page.tsx
import { redirect } from 'next/navigation'
 
async function fetchTeam(id: string) {
  const res = await fetch('https://...')
  if (!res.ok) return undefined
  return res.json()
}
 
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')
  }
 
  // ...
}

要知道:

  • redirect 預設傳回 307 (Temporary Redirect) 狀態碼。當在伺服器動作中使用時,它會傳回 303 (See Other),這通常用於將重新導向到成功頁面,作為 POST 請求的結果。
  • redirect 在內部拋出錯誤,因此應在 try/catch 區塊之外呼叫。
  • redirect 可以在渲染過程中在客戶端元件中呼叫,但不能在事件處理程序中呼叫。您可以改用 useRouter hook
  • redirect 也接受絕對 URL,可用於重新導向到外部連結。
  • 如果您想在渲染過程之前重新導向,請使用 next.config.js中介軟體

請參閱 redirect API 參考 以了解更多資訊。

使用原生 History API

Next.js 允許您使用原生 window.history.pushStatewindow.history.replaceState 方法,以更新瀏覽器的歷史堆疊,而無需重新載入頁面。

pushStatereplaceState 呼叫整合到 Next.js Router 中,讓您可以與 usePathnameuseSearchParams 同步。

window.history.pushState

使用它將新項目新增至瀏覽器的歷史堆疊。使用者可以導航回先前的狀態。例如,對產品列表進行排序

'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function SortProducts() {
  const searchParams = useSearchParams()
 
  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)
    window.history.pushState(null, '', `?${params.toString()}`)
  }
 
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  )
}

window.history.replaceState

使用它來替換瀏覽器歷史堆疊中的目前項目。使用者無法導航回先前的狀態。例如,切換應用程式的語系

'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale: string) {
    // e.g. '/en/about' or '/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }
 
  return (
    <>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('fr')}>French</button>
    </>
  )
}

路由和導航運作方式

App Router 對於路由和導航採用混合方法。在伺服器端,您的應用程式程式碼會依路由區段自動進行程式碼分割。在客戶端,Next.js 會預先提取快取路由區段。這表示,當使用者導航到新路由時,瀏覽器不會重新載入頁面,只有變更的路由區段會重新渲染 - 從而改善導航體驗和效能。

1. 程式碼分割

程式碼分割允許您將應用程式程式碼分割成較小的捆綁包,供瀏覽器下載和執行。這減少了每次請求傳輸的資料量和執行時間,從而提高了效能。

伺服器元件允許您的應用程式程式碼依路由區段自動進行程式碼分割。這表示只有目前路由所需的程式碼會在導航時載入。

2. 預先提取

預先提取是一種在使用者造訪路由之前,在背景預先載入路由的方式。

在 Next.js 中,有兩種預先提取路由的方式:

  • <Link> 元件:當路由在使用者視窗中可見時,會自動預先提取路由。預先提取會在頁面首次載入或透過滾動進入視圖時發生。
  • router.prefetch()useRouter hook 可以用來以程式化的方式預先提取路由。

<Link> 的預設預先提取行為(即,當 prefetch 屬性保持未指定或設定為 null 時)取決於您對 loading.js 的使用。只有共用版面配置,沿著元件的渲染「樹狀結構」向下直到第一個 loading.js 檔案,才會被預先提取並快取 30 秒。這降低了提取整個動態路由的成本,並且表示您可以顯示即時載入狀態,以獲得更好的視覺回饋給使用者。

您可以將 prefetch 屬性設定為 false 來停用預先提取。或者,您可以通過將 prefetch 屬性設定為 true 來預先提取超出載入邊界的全頁資料。

請參閱 <Link> API 參考 以了解更多資訊。

要知道:

  • 預先提取在開發環境中未啟用,僅在生產環境中啟用。

3. 快取

Next.js 有一個記憶體內客戶端快取,稱為 Router 快取。當使用者在應用程式中導航時,預先提取的路由區段和造訪路由的 React 伺服器元件 Payload 會儲存在快取中。

這表示在導航時,會盡可能重複使用快取,而不是向伺服器發出新的請求 - 通過減少請求數量和資料傳輸來提高效能。

深入了解 Router 快取 的運作方式以及如何設定它。

4. 部分渲染

部分渲染表示只有在導航時變更的路由區段才會在客戶端重新渲染,並且任何共用區段都會保留。

例如,當在兩個同級路由 /dashboard/settings/dashboard/analytics 之間導航時,settings 頁面將被卸載,analytics 頁面將以新的狀態掛載,並且共用的 dashboard 版面配置將被保留。這種行為也存在於同一動態區段上的兩個路由之間,例如 /blog/[slug]/page 和從 /blog/first 導航到 /blog/second

How partial rendering works

如果沒有部分渲染,每次導航都會導致整個頁面在客戶端重新渲染。僅渲染變更的區段可減少資料傳輸量和執行時間,從而提高效能。

5. 軟導航

瀏覽器在頁面之間導航時執行「硬導航」。Next.js App Router 在頁面之間啟用「軟導航」,確保只重新渲染已變更的路由區段(部分渲染)。這使得客戶端 React 狀態在導航期間得以保留。

6. 返回和前進導航

預設情況下,Next.js 將維護後退和前進導航的滾動位置,並在 Router 快取 中重複使用路由區段。

7. 在 pages/app/ 之間路由

當從 pages/ 增量遷移到 app/ 時,Next.js 路由將自動處理兩者之間的硬導航。為了檢測從 pages/app/ 的轉換,有一個客戶端路由篩選器,它利用應用程式路由的機率檢查,這偶爾可能導致誤判。預設情況下,這種情況應該非常罕見,因為我們將誤判可能性配置為 0.01%。此可能性可以透過 next.config.js 中的 experimental.clientRouterFilterAllowedRate 選項自訂。重要的是要注意,降低誤判率將增加客戶端捆綁包中產生的篩選器大小。

或者,如果您希望完全停用此處理並手動管理 pages/app/ 之間的路由,您可以將 next.config.js 中的 experimental.clientRouterFilter 設定為 false。當此功能停用時,預設情況下,頁面中與應用程式路由重疊的任何動態路由都無法正確導航。