现在开始了解Next.js 对更高效的layout布局支持 .. - Layouts RFC
这个React 模型允许我们结构一个页面到一系列组件 ..
许多这些组件能够在多个页面之间进行重用… 举个例子,你可能想要在每个页面上都有相同的导航条以及底部栏 ..
// components/layout.jsimport Navbar from './navbar'import Footer from './footer'export default function Layout({ children }) {return (<><Navbar /><main>{children}</main><Footer /></>)}
examples
自定义App的单个共享Layout
如果在整个应用中仅仅只有一个布局,我们能够创建一个自定义的App并通过layout包装应用 .. 当页面发生改变时重用<Layout />,它的组件状态仍然能够获得保留 ..
// pages/_app.jsimport Layout from '../components/layout'export default function MyApp({ Component, pageProps }) {return (<Layout><Component {...pageProps} /></Layout>)}
per-Page Layouts
如果存在多个布局,能够增加一个属性到页面上(getLayout),允许你针对layout 返回一个React 组件,这允许你定义一个layout - 基于每一个页面,因此我们能够返回一个函数,用于内嵌一个我们想要的复杂的布局 …
// pages/index.jsimport 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>)}
然后在pages/_app.js中
// pages/_app.jsexport default function MyApp({ Component, pageProps }) {// Use the layout defined at the page level, if availableconst getLayout = Component.getLayout || ((page) => page)return getLayout(<Component {...pageProps} />)}
当在多个页面进行导航的时候,我们想要持久化页面状态(输入值,滚动位置,以及其他),增加单页面应用的体验 ..
这个layout 模式能够启用状态持久化(因为React 组件树在页面过渡之间仍然是维持的,保留的),使用组件树,React 可以了解哪些元素已更改以保持状态。
这个过程叫做 reconciliation ,它表示React 如何理解那些元素发生了改变 。。
with TypeScript
当使用Ts的时候,你必须为页面创建一个新类型(包含getLayout函数的类型),然后创建一个新的AppProps类型,它会覆盖Component属性去使用之前创建的类型 …
假设一个页面是:
// pages/index.tsximport 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
然后对于布局,我们现在的类型就是
type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {getLayout?: (page: ReactElement) => ReactNode}
然后当前pages/_app.js的属性就应该是
type AppPropsWithLayout = AppProps & {Component: NextPageWithLayout}
最终组件渲染就变成了
// pages/_app.tsximport 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 availableconst getLayout = Component.getLayout ?? ((page) => page)return getLayout(<Component {...pageProps} />)}
数据抓取
在layout中,你能够通过useEffect,swr进行客户端数据抓取 ,因为这个文件不是一个页面(Page),你不能够使用getStaticProps,getServerSideProps
// components/layout.jsimport 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 /></>)}
