頁面與佈局

Pages Router 基於頁面的概念,提供了一個基於檔案系統的路由器。

當檔案被添加到 pages 目錄時,它會自動成為一個可用的路由。

在 Next.js 中,頁面 是一個從 pages 目錄中的 .js.jsx.ts.tsx 檔案匯出的 React 元件。每個頁面根據其檔案名稱與一個路由相關聯。

範例:如果您建立一個 pages/about.js 檔案並匯出如下的 React 元件,它將可以在 /about 路徑下被存取。

export default function About() {
  return <div>About</div>
}

索引路由

路由器會自動將名為 index 的檔案路由到目錄的根路徑。

  • pages/index.js/
  • pages/blog/index.js/blog

巢狀路由

路由器支援巢狀檔案結構。如果您建立了一個巢狀的資料夾結構,檔案仍會以相同的方式自動路由。

  • pages/blog/first-post.js/blog/first-post
  • pages/dashboard/settings/username.js/dashboard/settings/username

動態路由頁面

Next.js 支援具有動態路由的頁面。例如,如果您建立一個名為 pages/posts/[id].js 的檔案,那麼它將可以在 posts/1posts/2 等路徑下被存取。

若要了解更多關於動態路由的資訊,請參閱 動態路由文件

佈局模式

React 模型允許我們將 頁面 分解為一系列元件。這些元件中的許多部分通常會在頁面之間重複使用。例如,您可能在每個頁面上都有相同的導覽列和頁尾。

components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

範例

使用自訂 App 的單一共享佈局

如果您整個應用程式只有一個佈局,您可以建立一個 自訂 App 並用佈局包裹您的應用程式。由於 <Layout /> 元件在切換頁面時會被重複使用,其元件狀態(例如輸入值)將被保留。

pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

每個頁面的佈局

如果您需要多個佈局,可以在頁面上添加一個 getLayout 屬性,允許您返回一個 React 元件作為佈局。這讓您可以根據 每個頁面的需求 定義佈局。由於我們返回的是一個函數,如果需要,我們可以擁有複雜的巢狀佈局。

pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return (
    /** 您的內容 */
  )
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // 使用頁面層級定義的佈局(如果有的話)
  const getLayout = Component.getLayout || ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

在頁面之間導航時,我們希望 保留 頁面狀態(輸入值、滾動位置等)以實現單頁應用程式 (SPA) 的體驗。

這種佈局模式能夠實現狀態保留,因為 React 元件樹在頁面轉換之間保持不變。有了元件樹,React 可以理解哪些元素發生了變化以保留狀態。

須知:這個過程稱為 協調 (reconciliation),這是 React 理解哪些元素發生變化的方式。

使用 TypeScript

當使用 TypeScript 時,您必須首先為您的頁面建立一個包含 getLayout 函數的新類型。然後,您必須為您的 AppProps 建立一個新類型,覆蓋 Component 屬性以使用先前建立的類型。

import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
  return <p>hello world</p>
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

export default Page

資料獲取

在您的佈局中,您可以使用 useEffect 或像 SWR 這樣的函式庫在客戶端獲取資料。由於這個檔案不是 頁面,您目前無法使用 getStaticPropsgetServerSideProps

components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  const { data, error } = useSWR('/api/navigation', fetcher)

  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>

  return (
    <>
      <Navbar links={data.links} />
      <main>{children}</main>
      <Footer />
    </>
  )
}

On this page