页面与布局

Pages Router 基于页面概念构建了一个基于文件系统的路由系统。

当文件被添加到 pages 目录时,它会自动成为可访问的路由。

在 Next.js 中,页面 (page) 是从 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 />
    </>
  )
}

示例

使用 Custom App 的单一共享布局

如果您的整个应用只有一个布局,可以创建一个 Custom 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

数据获取

在布局中,您可以使用 useEffectSWR 等库在客户端获取数据。由于此文件不是 页面 (Page),目前无法使用 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>加载失败</div>
  if (!data) return <div>加载中...</div>

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

On this page