Next.js 核心功能详解
1. 全局模板 (_app.js)
问题: 很多页面都有共同的布局,如页头、页脚、导航栏。在每个页面组件中重复编写这些代码非常繁琐。
解决方案: Next.js 提供了 pages/_app.js (或 .jsx) 文件作为全局应用模板。这个组件会包裹所有页面组件,是实现持久化布局、注入全局 CSS、传递共享状态等的理想位置。
实现:
- 在
pages目录下创建一个名为_app.jsx的文件。 - 创建一个自定义的
App组件。它会接收两个核心 props:Component: 当前匹配到的页面组件(例如HomePage,MoviesPage)。pageProps: 传递给该页面组件的 props(在后续数据获取章节中会详细讲解)。
// pages/_app.jsx
import Header from '../components/Header'; // 假设我们有一个公共头部组件
function MyApp({ Component, pageProps }) {
// 在这里放置所有页面的通用布局
return (
<div>
<h1>这是我的全局模板</h1>
<Header />
<hr />
{/* 渲染当前页面组件,并将 props 传递给它 */}
<Component {...pageProps} />
</div>
);
}
export default MyApp;效果: 现在,应用中的每一个页面都会被 MyApp 组件包裹,自动拥有了模板中定义的布局和组件。
2. 动态路由
问题: 如何创建 URL 中包含动态参数的页面,例如 /movies/123 用于展示 ID 为 123 的电影详情?
解决方案: Next.js 使用中括号 [] 语法在文件名中定义动态路由段。
2.1. 单个动态参数
- 文件结构:
pages/movies/[id].jsx - 路由匹配: 这个文件会匹配所有形如
/movies/任意字符串的路径,例如/movies/1,/movies/abc。
获取参数: 使用 next/router 提供的 useRouter Hook 来访问路由信息。动态参数会存在于 router.query 对象中。
// pages/movies/[id].jsx
import { useRouter } from 'next/router';
const MovieDetailPage = () => {
const router = useRouter();
const { id } = router.query; // 获取文件名 [id] 对应的参数值
return <h1>电影详情页, ID: {id}</h1>;
};
export default MovieDetailPage;注意:
router.query同时包含动态路由参数(如id)和 URL 查询字符串参数(如?name=...)。如果两者重名,动态路由参数的优先级更高。
2.2. 捕获所有路由 (Catch-all)
问题: 如何匹配一个路径下的所有子路径,例如 /docs/a/b/c?
解决方案: 使用带有三个点的中括号语法 [...param]。
- 文件结构:
pages/docs/[...params].jsx - 路由匹配: 会匹配
/docs/后面的所有路径段。 - 获取参数:
router.query.params会是一个包含所有匹配段的数组。例如,访问/docs/a/b/c,则router.query.params的值为['a', 'b', 'c']。
3. 页面导航 (next/link)
Next.js 使用 <Link> 组件来实现客户端路由(无刷新页面跳转)。
3.1. 基础用法
// components/Header.jsx
import Link from 'next/link';
export default function Header() {
return (
<nav>
<Link href="/">
<a>首页</a>
</Link>
<Link href="/movies">
<a>电影页</a>
</Link>
</nav>
);
}注意:
<Link>组件内部必须包裹一个<a>标签或其他可以接收onClick事件的元素。
3.2. 动态路由的链接 (重要)
问题: 链接到一个动态路由(如 /movies/3)时,如果直接写 href="/movies/3" 会导致页面刷新,失去客户端导航的优势。
解决方案: 必须同时使用 href 和 as 两个属性。
href: 指向pages目录中对应的文件路径模板。as: 指向浏览器地址栏中实际显示的友好 URL。
// 正确的动态路由链接
<Link href="/movies/[id]" as="/movies/3">
<a>电影详情页 (ID:3)</a>
</Link>这个语法告诉 Next.js:“这个链接在浏览器中显示为 /movies/3,但请使用 pages/movies/[id].jsx 这个组件来渲染它”。
3.3. 命令式导航 (代码跳转)
使用 useRouter Hook 获取 router 对象,然后调用其 push 或 replace 方法。
import { useRouter } from 'next/router';
function MyComponent() {
const router = useRouter();
const handleJump = () => {
// 跳转到动态路由
router.push({
pathname: '/movies/[id]',
query: { id: 4 },
});
};
return <button onClick={handleJump}>跳转到电影详情页 (ID:4)</button>;
}4. 自定义错误页面
4.1. 404 页面 (Not Found)
- 实现: 在
pages目录下创建一个名为404.jsx的文件。 - 效果: 当用户访问一个不存在的路由时,Next.js 会自动渲染这个组件,并正确地返回
404HTTP 状态码。
// pages/404.jsx
export default function Custom404() {
return <h1>404 - 页面未找到</h1>;
}4.2. 500 页面 (Server Error)
- 实现: 在
pages目录下创建一个名为_error.jsx的文件。 - 效果:
- 在生产环境中,当页面渲染时发生服务器端错误,Next.js 会渲染这个组件。
- 在开发环境中,为了便于调试,Next.js 会显示详细的错误堆栈信息,而不是这个自定义页面。
- 如果
404.js不存在,_error.js也会作为 404 页面的降级方案。