页面和布局
页面路由有一个基于页面概念的基于文件系统的路由。
¥The Pages Router has a file-system based router built on the concept of pages.
当文件添加到 pages
目录时,它会自动作为路由使用。
¥When a file is added to the pages
directory, it's automatically available as a route.
在 Next.js 中,页面是从 pages
目录中的 .js
、.jsx
、.ts
或 .tsx
文件导出的 React 组件。每个页面都根据其文件名与一个路径相关联。
¥In Next.js, a page is a React Component exported from a .js
, .jsx
, .ts
, or .tsx
file in the pages
directory. Each page is associated with a route based on its file name.
示例:如果你创建像下面这样导出 React 组件的 pages/about.js
,则可以在 /about
访问该组件。
¥Example: If you create pages/about.js
that exports a React component like below, it will be accessible at /about
.
export default function About() {
return <div>About</div>
}
索引路由
¥Index routes
路由会自动将名为 index
的文件路由到目录的根目录。
¥The router will automatically route files named index
to the root of the directory.
-
pages/index.js
→/
-
pages/blog/index.js
→/blog
嵌套路由
¥Nested routes
路由支持嵌套文件。如果你创建嵌套文件夹结构,文件仍将以相同的方式自动路由。
¥The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still.
-
pages/blog/first-post.js
→/blog/first-post
-
pages/dashboard/settings/username.js
→/dashboard/settings/username
具有动态路由的页面
¥Pages with Dynamic Routes
Next.js 支持具有动态路由的页面。例如,如果你创建一个名为 pages/posts/[id].js
的文件,则可以在 posts/1
、posts/2
等处访问该文件。
¥Next.js supports pages with dynamic routes. For example, if you create a file called pages/posts/[id].js
, then it will be accessible at posts/1
, posts/2
, etc.
要了解有关动态路由的更多信息,请查看 动态路由文档。
¥To learn more about dynamic routing, check the Dynamic Routing documentation.
布局模式
¥Layout Pattern
React 模型允许我们将 page 解构为一系列组件。其中许多组件经常在页面之间重用。例如,你可能在每个页面上都有相同的导航栏和页脚。
¥The React model allows us to deconstruct a page into a series of components. Many of these components are often reused between pages. For example, you might have the same navigation bar and footer on every page.
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
示例
¥Examples
具有自定义应用的单一共享布局
¥Single Shared Layout with Custom App
如果你的整个应用只有一个布局,则可以创建 自定义应用 并使用该布局封装你的应用。由于 <Layout />
组件在更改页面时被重用,因此其组件状态将被保留(例如输入值)。
¥If you only have one layout for your entire application, you can create a Custom App and wrap your application with the layout. Since the <Layout />
component is re-used when changing pages, its component state will be preserved (e.g. input values).
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
每页布局
¥Per-Page Layouts
如果你需要多个布局,你可以向页面添加属性 getLayout
,允许你返回布局的 React 组件。这允许你在每页的基础上定义布局。由于我们返回一个函数,因此如果需要,我们可以拥有复杂的嵌套布局。
¥If you need multiple layouts, you can add a property getLayout
to your page, allowing you to return a React component for the layout. This allows you to define the layout on a per-page basis. Since we're returning a function, we can have complex nested layouts if desired.
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** Your content */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
在页面之间导航时,我们希望保留页面状态(输入值、滚动位置等)以获得单页应用 (SPA) 体验。
¥When navigating between pages, we want to persist page state (input values, scroll position, etc.) for a Single-Page Application (SPA) experience.
这种布局模式可以实现状态持久化,因为 React 组件树是在页面转换之间维护的。通过组件树,React 可以了解哪些元素已更改以保留状态。
¥This layout pattern enables state persistence because the React component tree is maintained between page transitions. With the component tree, React can understand which elements have changed to preserve state.
很高兴知道:这个过程称为 reconciliation,React 就是通过它来了解哪些元素发生了变化。
¥Good to know: This process is called reconciliation, which is how React understands which elements have changed.
使用 TypeScript
¥With TypeScript
使用 TypeScript 时,你必须首先为页面创建一个包含 getLayout
函数的新类型。然后,你必须为 AppProps
创建一个新类型,该类型将覆盖 Component
属性以使用先前创建的类型。
¥When using TypeScript, you must first create a new type for your pages which includes a getLayout
function. Then, you must create a new type for your AppProps
which overrides the Component
property to use the previously created type.
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
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
const Page = () => {
return <p>hello world</p>
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default Page
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
数据获取
¥Data Fetching
在布局内部,你可以使用 useEffect
或 SWR 这样的库在客户端获取数据。由于该文件不是 页面,因此当前无法使用 getStaticProps
或 getServerSideProps
。
¥Inside your layout, you can fetch data on the client-side using useEffect
or a library like SWR. Because this file is not a Page, you cannot use getStaticProps
or getServerSideProps
currently.
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 />
</>
)
}