跳至內容

連結與導覽

在 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.js
'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: { id: string } }) {
  const team = await fetchTeam(params.id)
  if (!team) {
    redirect('/login')
  }
 
  // ...
}

注意事項:

  • redirect 預設會返回 307(暫時重新導向)狀態碼。在伺服器動作中使用時,它會返回 303(查看其他),這通常用於將 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>
    </>
  )
}

路由與導覽的運作方式

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

1. 程式碼分割

程式碼分割允許您將應用程式程式碼分割成更小的 bundle,以便瀏覽器下載和執行。這減少了每次請求的數據傳輸量和執行時間,從而提高了效能。

伺服器組件允許您的應用程式程式碼根據路由區段自動進行程式碼分割。這意味著導航時只會載入當前路由所需的程式碼。

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 有一個稱為 路由器快取記憶體內用戶端快取。當使用者在應用程式中導航時,預取 的路由區段和已訪問路由的 React 伺服器組件有效負載會儲存在快取中。

這意味著在導航時,會盡可能重複使用快取,而不是向伺服器發出新的請求 — 通過減少請求次數和數據傳輸量來提高效能。

深入瞭解 路由器快取 的運作方式以及如何設定它。

4. 部分渲染

部分渲染意味著只有在導航時變更的路由區段才會在用戶端重新渲染,而任何共用區段都會被保留。

舉例來說,當在兩個同級路由(sibling routes)之間導覽,例如從 /dashboard/settings/dashboard/analyticssettings 頁面會被卸載,analytics 頁面會以全新的狀態被掛載,而共用的 dashboard 版面配置會被保留。相同的動態區段中的兩個路由之間也會出現這種行為,例如 /blog/[slug]/page,從 /blog/first 導覽到 /blog/second 時。

How partial rendering works

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

5. 軟性導覽

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

6. 前進和後退導覽

預設情況下,Next.js 會在前進和後退導覽時維持捲動位置,並重複使用路由器快取中的路由區段。

7. 在 pages/app/ 之間的路由

當從 pages/ 逐步遷移到 app/ 時,Next.js 路由器會自動處理兩者之間的硬性導覽。為了偵測從 pages/app/ 的轉換,我們使用了一個客戶端路由過濾器,它利用機率檢查 app 路由,這偶爾會導致誤判。預設情況下,這種情況應該非常罕見,因為我們將誤判的可能性設定為 0.01%。這個可能性可以透過 next.config.js 中的 experimental.clientRouterFilterAllowedRate 選項進行自訂。需要注意的是,降低誤判率會增加客戶端套件中產生的過濾器的大小。

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