页面与布局
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-postpages/dashboard/settings/username.js→/dashboard/settings/username
带动态路由的页面
Next.js 支持带动态路由的页面。例如,如果你创建了一个名为 pages/posts/[id].js 的文件,那么它将在 posts/1、posts/2 等处可访问。
要了解更多关于动态路由的信息,请查阅 动态路由文档。
布局模式
React 模型允许我们将页面分解成一系列组件。其中许多组件经常在页面之间重复使用。例如,你可能在每个页面上都有相同的导航栏和页脚。
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}示例
带有自定义 App 的单一共享布局
如果你的整个应用只有一个布局,你可以创建一个自定义 App 并用该布局包裹你的应用。由于 <Layout /> 组件在页面切换时会被重复使用,它的组件状态将被保留(例如输入值)。
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}按页面布局
如果你需要多个布局,你可以给你的页面添加一个 getLayout 属性,允许你返回一个 React 组件作为布局。这允许你按页面定义布局。由于我们返回的是一个函数,如果需要的话,我们可以有复杂的嵌套布局。
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)体验保留页面状态(输入值、滚动位置等)。
这种布局模式能够实现状态持久化,因为 React 组件树在页面转换之间保持不变。通过组件树,React 可以理解哪些元素已更改以保留状态。
小知识:这个过程被称为 协调,它是 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 Pageimport 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} />)
}数据获取
在你的布局中,你可以使用 useEffect 或像 SWR 这样的库在客户端获取数据。由于此文件不是页面,你目前不能使用 getStaticProps 或 getServerSideProps。
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 />
</>
)
}这有帮助吗?