动态路由
动态段作为 params
属性传递给 layout
、page
、route
和 generateMetadata
函数。
/app/blog/[slug]/page.tsx
export default function Page({params}: {params:{slug:string}}) {
return <h1>Slug Page -- {params.slug}</h1>
};
/app/shop/[...slug]/page.tsx
export default function Page({params}: {params:{slug:string}}) {
return <h1>Catch All Slug Page -- {params.slug}</h1>
};
[...slug]
和 [[...slug]]
不能放在同一目录下。而且他们用法相似。
catch-all 和 option-catch-all 的区别在于,如果带可选参数,不带参数的路由也会被匹配(上例中的 /shop
)。
generateStaticParams
功能可以在构建时与 动态路线段 至 静态生成 路由结合使用,而不是在请求时按需使用。
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
如果使用 fetch
请求在 generateStaticParams
函数内获取内容,则请求为 自动记忆。 这意味着跨多个 generateStaticParams
、布局和页面具有相同参数的 fetch
请求只会发出一次,从而减少构建时间。
加载 UI 和流式传输
在加载路线段内容时显示来自服务器的 即时加载状态。 渲染完成后,新内容会自动换入。
下面的 loading.tsx 是预渲染加载指示器。例如骨架和旋转器,或未来屏幕的一小部分但有意义的部分,例如封面照片、标题等。这有助于用户了解应用正在响应,并提供更好的用户体验。
Suspense 的流式使得页面的某些部分能够更快地显示,而无需等待所有数据加载后才能渲染任何 UI。
当你想要防止长数据请求阻止页面渲染时,流式处理特别有用,因为它可以减少 第一个字节的时间 (TTFB) 和 首次内容绘制 (FCP)。 它还有助于提高 互动时间 (TTI),尤其是在速度较慢的设备上。
/app/loading.tsx
export default function Loading() {
return (
<>
<div className={"text-2xl text-yellow-700"}>Loading...</div>
</>
)
};
/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import {Suspense} from "react";
import Loading from "@/app/loading";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Suspense fallback={<Loading />}>
RootLayout
{children}
</Suspense>
</body>
</html>
);
}