Next.js App Router 核心特性
文件系统路由、特殊文件约定、generateStaticParams 静态生成、Metadata API——搭博客必须掌握的 Next.js 特性。
从零搭建 Next.js 博客
- 1前端工具链入门:npm、pnpm、npx 是什么
- 2JavaScript 核心语法速览
- 3TypeScript 核心语法:给 JS 加上类型
- 4React 组件基础:JSX、Props、导入导出
- 5Next.js App Router 核心特性
Next.js App Router 核心特性
Next.js 13+ 引入了 App Router,这是目前推荐的开发方式。本文介绍搭建博客过程中用到的几个核心特性。
1. 文件系统路由
App Router 基于 app/ 目录,文件路径就是 URL 路径:
app/
page.tsx → /
about/
page.tsx → /about
posts/
page.tsx → /posts
[slug]/
page.tsx → /posts/:slug (动态路由)
tags/
[tag]/
page.tsx → /tags/:tag
layout.tsx → 所有页面共享的布局
[slug] 里的方括号表示这是个动态路由参数,访问 /posts/hello-world 时,slug 的值就是 "hello-world"。
2. 特殊文件约定
在任意路由目录下,Next.js 识别这些特殊文件名:
| 文件名 | 作用 |
|---|---|
page.tsx |
页面组件,URL 可访问 |
layout.tsx |
布局,包裹子页面,切换页面时不会重新渲染 |
loading.tsx |
页面加载中时显示的 UI(自动配合 Suspense) |
error.tsx |
页面报错时显示的 UI |
not-found.tsx |
调用 notFound() 时显示的 404 页面 |
博客里用到了 layout.tsx(放 Header/Footer)和通过 notFound() 触发的 404。
3. 动态路由页面的类型写法
Next.js 15+ 中,params 是 Promise 类型,需要 await:
// app/posts/[slug]/page.tsx
interface PageProps {
params: Promise<{ slug: string }>;
}
export default async function PostPage({ params }: PageProps) {
const { slug } = await params; // 必须 await
const post = await getPostBySlug(slug);
return <article>{post.title}</article>;
}
4. generateStaticParams:静态预生成
博客文章是静态内容,构建时就能生成好所有 HTML,不需要每次请求都重新生成。generateStaticParams 告诉 Next.js 有哪些动态路由需要预生成:
// 告诉 Next.js:预先生成这些路径
// /posts/hello-world
// /posts/typescript-basics
// /posts/nextjs-core-features
// ...
export async function generateStaticParams() {
const slugs = getAllSlugs(); // 读取所有文章的 slug
return slugs.map((slug) => ({ slug }));
// 返回:[{ slug: "hello-world" }, { slug: "typescript-basics" }, ...]
}
优点: 所有文章页面在构建时就生成为静态 HTML,访问速度极快,对 SEO 非常友好。
5. Metadata API:动态 SEO
Next.js 提供了 generateMetadata 函数,自动为每个页面生成 <head> 里的 SEO 标签:
// 静态 metadata(适合不变的页面)
export const metadata = {
title: "关于我",
description: "关于这个博客",
};
// 动态 metadata(适合需要根据数据生成的页面,比如文章详情)
export async function generateMetadata({ params }: PageProps) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return {
title: post.title, // → <title>文章标题</title>
description: post.excerpt, // → <meta name="description" ...>
};
}
在 layout.tsx 里设置了 template,子页面的标题会自动套上格式:
// layout.tsx
export const metadata = {
title: {
default: "Mary's Blog",
template: "%s | Mary's Blog", // %s 被子页面标题替换
},
};
// 文章页设置 title: "TypeScript 入门"
// 最终浏览器标签显示:TypeScript 入门 | Mary's Blog
6. notFound():触发 404
访问不存在的文章时,调用 notFound() 渲染 404 页面:
import { notFound } from "next/navigation";
export default async function PostPage({ params }: PageProps) {
const { slug } = await params;
let post;
try {
post = await getPostBySlug(slug);
} catch {
notFound(); // 找不到文章,触发 404
}
return <article>{post.title}</article>;
}
如果目录下有 not-found.tsx,会渲染那个组件;否则用 Next.js 默认的 404 页面。
这五个特性是 App Router 的核心,把它们组合起来就构成了整个博客的路由和数据层。