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