已发布·持续改进
Next.js 指南 · 3/6
本章目前仅提供英文版。
A handful of ideas carry most of the App Router: nested layouts, Server and Client Components, Server Actions, rendering modes and metadata.
A layout receives its child route as children and wraps it. Any folder can have a layout, and layouts nest: the outer one wraps the inner one. Navigating between pages under the same layout keeps the layout and its state, so it is the place for navigation bars and sidebars.
// app/dashboard/layout.tsx
import Link from "next/link";
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<section>
<nav>
<Link href="/dashboard">Overview</Link>
<Link href="/dashboard/settings">Settings</Link>
</nav>
<div>{children}</div>
</section>
);
}In the App Router, layouts and pages are Server Components by default. They run on the server and send only their result to the browser, so their code and imports never reach the client bundle, and they can query a database or use an API key safely.
When you need hooks such as useState or useEffect, event handlers such as onClick, or browser APIs such as window and localStorage, use a Client Component. Adding the "use client" directive at the top of a file puts that file, and the modules it imports, into the browser bundle.
// app/ui/like-button.tsx
"use client";
import { useState } from "react";
export function LikeButton({ initial }: { initial: number }) {
const [likes, setLikes] = useState(initial);
return <button onClick={() => setLikes(likes + 1)}>Likes {likes}</button>;
}Server Components can import and render Client Components. Props passed across that boundary must be serializable (strings, numbers, plain objects, arrays and so on); functions cannot be passed, with Server Actions as the exception.
// app/posts/[id]/page.tsx (a Server Component)
import { LikeButton } from "@/app/ui/like-button";
import { getPost } from "@/lib/posts";
export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const post = await getPost(id);
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
<LikeButton initial={post.likes} />
</article>
);
}The recommended pattern is to push the "use client" boundary as far toward the leaves of the tree as possible. Keep only the interactive pieces, such as a button or a form, on the client, and you ship less JavaScript. To render server-generated content inside a Client Component, pass it in as children.
A Server Action (more generally, a Server Function) is an async function marked with "use server". It is called from the browser but runs on the server, handling forms and mutations without a separate API route. Passed to a form's action prop, it works even before JavaScript loads.
// app/guestbook/page.tsx
import { revalidatePath } from "next/cache";
import { db } from "@/lib/db";
export default function GuestbookPage() {
async function addEntry(formData: FormData) {
"use server";
const message = String(formData.get("message") ?? "");
await db.entry.create({ data: { message } });
revalidatePath("/guestbook");
}
return (
<form action={addEntry}>
<input name="message" required />
<button type="submit">Sign</button>
</form>
);
}Treat every Server Action as a public endpoint: check auth and validate input inside it.
Next.js decides, route by route, when HTML is produced.
| Mode | When HTML is produced | Good fit |
|---|---|---|
| Static rendering | Ahead of time, at build or revalidation | Marketing pages, blog posts, docs |
| Dynamic rendering | On every request | Personalized dashboards, search results |
| Streaming | In chunks, as parts become ready | Pages that mix fast and slow data |
Reading request-time information such as cookies(), headers() or search params, or reading uncached data, makes that part render at request time. Wrap slow parts in <Suspense>, or add a loading.tsx next to the page, and Next.js sends the rest of the page first and streams the remaining content as it resolves.
// app/dashboard/page.tsx
import { Suspense } from "react";
import { RecentOrders } from "./recent-orders";
export default function DashboardPage() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading orders...</p>}>
<RecentOrders />
</Suspense>
</main>
);
}Recent releases also offer Cache Components (the cacheComponents option), which mixes a static shell with dynamic parts on one page. This area is evolving, so check the docs for your version.
Export a metadata object from a layout or page to set the title, description and Open Graph tags, or a generateMetadata function when they depend on data. Both are supported only in Server Components.
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
import { getPost } from "@/lib/posts";
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return { title: post.title, description: post.summary };
}"use client".Suspense or loading.tsx enables streaming.metadata and generateMetadata to give every page proper search information.
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。