在上一部分,我们成功搭建了开发环境。现在让我们深入了解 Next.js 的路由系统。App Router 是 Next.js 最重要的特性之一,它基于文件系统,让路由变得直观而强大。

App Router 采用了一种“约定优于配置”的设计理念。这意味着你不需要手动配置路由表,只需要按照特定的文件命名规则创建文件,Next.js 就会自动为你生成对应的路由。
这种设计带来的好处是多方面的。首先,路由结构一目了然,你只需要查看 app 目录就能知道整个应用的路由结构。其次,文件系统路由天然支持嵌套路由,这让构建复杂的应用变得简单。最后,每个路由段都可以有自己的布局、加载状态和错误处理,这提供了极大的灵活性。
让我们从最基础的规则开始。在 app 目录下,page.tsx 文件定义了该路由段的页面内容。例如,app/page.tsx 对应根路径 /,而 app/about/page.tsx 则对应 /about 路径。
路由的层级关系由文件夹的嵌套结构决定。如果你创建了 app/courses/web/page.tsx,那么访问 /courses/web 时就会渲染这个页面。这种嵌套关系是自动的,你不需要任何额外配置。
在 App Router 中,只有 page.tsx 文件会创建路由。其他文件如 layout.tsx、loading.tsx 等不会创建路由,它们只是为路由提供额外的功能。
让我们开始创建一些页面。首先,我们来看看默认的首页 app/page.tsx:
|export default function Home() { return ( <div className="min-h-screen bg-linear-to-br from-blue-50 to-indigo-100"> <div className="container mx-auto px-4 py-16"> <h1 className="text-4xl font-bold text-center text-gray-900"> Welcome to Free Education </h1> </div> </div> ) }
这是一个简单的服务器组件。在 Next.js 中,默认情况下所有组件都是服务器组件。所谓“服务器组件”,就是这些组件的代码和渲染过程都发生在服务器端,生成好 HTML 后再返回给浏览器。
这样做有两个好处:首先,用户打开页面时,服务器已经把完整的内容渲染好,所以页面加载速度会更快,用户体验更好;其次,搜索引擎能够直接抓取到你的完整页面内容,有助于提升 SEO 效果。
只有你明确使用 use client 指令时,组件才会作为客户端组件运行,代码才会发送到浏览器。
现在让我们创建一个“关于我们”页面。在 app 目录下创建一个新的文件夹 about,然后在其中创建 page.tsx 文件:
|export default function About() { return ( <div className="min-h-screen bg-linear-to-br from-green-50 to-emerald-100"> <div className="container mx-auto px-4 py-16"> <h1 className="text-4xl font-bold text-center text-gray-900 mb-8"> 关于 Free Education </h1> <p className="text-lg text-gray-700 max-w-2xl mx-auto text-center"> Free Education 致力于为每个人提供免费、高质量的教育资源。 我们相信教育应该是开放的、可访问的,无论你的背景如何。
保存文件后,访问 http://localhost:3000/about,你应该能看到新创建的页面。这就是 App Router 的魅力——创建文件就能自动获得路由。
布局组件(Layout Components)是 App Router 的另一个重要特性。layout.tsx 文件定义了共享的布局结构,它会被该目录及其所有子目录的页面使用。
让我们看看根布局 app/layout.tsx,这个文件是 Next.js 自动生成的,我们不需要手动创建, 现在将里面的内容替换成我们下面的内容:
|import type { Metadata } from 'next' export const metadata: Metadata = { title: 'Free Education', description: '免费教育资源平台', } export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <
这个布局包裹了整个应用。children 属性代表当前路由的页面内容。当你访问不同的页面时,children 会替换为对应页面的内容,但布局结构保持不变。
布局的一个重要特性是它们不会在路由切换时重新渲染。这意味着你可以在布局中放置导航栏、页脚等共享组件,它们会保持状态,提供流畅的用户体验。
让我们为“关于”页面创建一个专门的布局。在 app/about 目录下创建 layout.tsx:
|export default function AboutLayout({ children, }: { children: React.ReactNode }) { return ( <div className="min-h-screen"> <div className="bg-green-600 text-white py-4"> <div className="container mx-auto px-4"> <h2 className
当我们在 app/about 目录下添加了 layout.tsx 文件后,这个文件会自动成为 app/about/page.tsx 以及 app/about 目录下所有子页面的布局容器。简单来说,每次访问“关于我们”相关页面时,页面内容(也就是 children)都会被插入到这个布局结构中,实现页面的统一外观和功能。
另外一个很重要的点是——布局可以多层嵌套。比如你在根目录有一个 app/layout.tsx,在 app/about 目录下有另一个 layout.tsx,那么“关于”页面最后显示时,其结构会是“根布局包裹着关于布局,再包裹实际页面内容”。这种父子嵌套结构让我们能够灵活地对不同区域设置不同的样式和功能,同时又保证了代码的清晰与复用。
现在我们已经有了多个页面,让我们添加导航功能,让用户可以在页面间切换。我们需要使用 Next.js 的 Link 组件。
首先,让我们更新我们的根布局(/app/layout.tsx),添加一个简单的导航栏:
|import Link from 'next/link' import type { Metadata } from 'next' export const metadata: Metadata = { title: 'Free Education', description: '免费教育资源平台', } export default function RootLayout({ children, }: { children: React.ReactNode
这里我们使用了 Next.js 的 Link 组件而不是普通的 <a> 标签。Link 组件提供了客户端导航,这意味着页面切换时不会完全重新加载,而是使用 JavaScript 进行路由跳转,这提供了更快的用户体验。
虽然你可以使用普通的 <a> 标签,但建议始终使用 Link 组件进行内部导航。Link 组件会提前获取链接的页面,当用户点击时,页面已经准备好了,这可以大大提升导航速度。
在平时开发中,我们会发现,单纯的静态路由只能应对页面路径是固定的情况,比如 /about 或 /contact。但是,很多情况下,页面路径是根据内容动态变化的。
举个例子,假如你有一个在线课程平台,每门课程的详情页地址都是 /courses/课程编号,像 /courses/javascript、/courses/python 这种。这时我们就需要用到“动态路由”。
Next.js 给我们提供了一种很简单的方式来实现动态路由:就是在文件夹或者文件名中使用方括号 []。只要你在 app 目录下,比如 courses 文件夹里面,新建一个名字为 [id] 的文件夹(这里的 id 其实就是变量名,代表每个不同的课程),然后在 [id] 文件夹里新建一个 page.tsx 文件,然后在文件中使用 params 对象来获取 id 参数就可以了。
通过这么做,不管你访问 /courses/javascript 还是 /courses/python,最终都会渲染同一个页面组件,不过其中的 id 参数会自动变成你访问的那个课程的名字或者编号,非常方便。
现在让我们创建一个课程详情页面(app/courses/[id]/page.tsx):
|export default function CourseDetail({ params, }: { params: { id: string } }) { return ( <div className="min-h-screen bg-gradient-to-br from-purple-50 to-pink-100"> <div className="container mx-auto px-4 py-16"> <h1 className="text-4xl font-bold text-center text-gray-900 mb-8"> 课程详情:{params.id}
现在你可以访问 /courses/javascript 或 /courses/python 等任何路径,都会渲染这个页面,并且 params.id 会包含对应的路径值。
有时候我们希望某些路由段是可选的。Next.js 支持可选路由段,使用双括号 [[...]] 来创建。
例如,如果我们想创建一个博客页面,其中分类是可选的,可以这样创建:
|// app/blog/[[...slug]]/page.tsx export default function Blog({ params, }: { params: { slug?: string[] } }) { if (!params.slug) { return ( <div className="min-h-screen bg-blue-50 p-16"> <h1 className="text-4xl font-bold">所有博客文章</h1>
这样,/blog、/blog/tech、/blog/tech/react 等路径都可以正常工作。
有时候我们想要组织路由,但不影响 URL 结构。这时可以使用路由组,通过使用括号 () 来创建。
例如,我们可以将管理相关的页面组织在一起:
|app/ (admin)/ dashboard/ page.tsx settings/ page.tsx
这样,dashboard 和 settings 的 URL 仍然是 /dashboard 和 /settings,但它们在文件系统中被组织在一起了。
现在让我们创建一个课程列表页面来实践这些概念。让我们创建 app/courses/page.tsx 来做课程的汇总展示:
|import Link from 'next/link' const courses = [ { id: 'javascript', name: 'JavaScript 基础', description: '学习 JavaScript 的核心概念' }, { id: 'python', name: 'Python 编程', description: '从零开始学习 Python' }, { id: 'react', name: 'React 开发', description: '构建现代 Web 应用' }, ] export default function Courses() { return ( <
然后在导航栏中添加课程链接:
|<Link href="/courses" className="text-gray-600 hover:text-gray-900"> 课程 </Link>
现在你可以在首页、关于页面和课程页面之间自由导航了。
在这一部分里,我们学习了 Next.js 的路由系统,包括静态路由、动态路由、布局组件和导航。在下一节中,我们将花点时间学习 Tailwind CSS,学习如何创建美观的样式和响应式设计。
你已经掌握了 Next.js 的路由系统!现在你可以创建多个页面,实现页面间的导航,甚至处理动态路由。这些知识日后将帮助你构建更复杂的应用。