技术篇:Next.js 路由与页面管理
Next.js实战教程 - 系列文章
学习目标:可以用 Next.js 开发页面和接口
在现代 Web 开发中,路由和页面管理是应用程序的核心部分。Next.js 13 引入了新的路由系统,称为 App Router,它使页面管理的灵活性和功能性增强。本文将深入探讨 Next.js 13 的路由机制及其如何简化页面管理,使开发者能够更高效地构建复杂的 Web 应用。
现在我们把问题进行简化,我们需要在浏览器展示的无非就是:页面(Page) + 数据(API)。我们只要把页面和数据解决就可以基于 Next.js 13 开发应用了。 页面就是要展示的 html 页面,API 可以理解为数据接口。下面我们开始详细介绍。
npx create-next-app@latest
✔ What is your project named? … example-route
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
页面 #
路由与文件结构 #
Next.js 13 的路由系统依赖于文件系统的结构,这意味着你的应用的 URL 结构与 app/
目录中的文件和文件夹结构直接相关。先看一个最直观的例子:
下图是初始化项目后的默认结构,app/page.js
就是我们项目默认的首页。
我们直接运行上面初始化的项目:yarn dev
可以看到显示的即 app/page.js
的内容。我们修改app/page.js
,同步可以在浏览器看的相应的变化。
文件系统路由 #
Next.js 13 的文件系统路由基于 app/
目录的层级结构。每个文件或文件夹代表一个路由。例如:
app/page.js
对应根路径/
。app/about/page.js
对应/about
路由。app/blog/[id]/page.js
对应动态路由/blog/:id
,其中id
是一个动态参数。app/layout.js
是默认的全局布局,所有页面都将继承它。
这种文件结构使得你可以通过简单的文件和文件夹操作来管理路由,减少了手动配置的需求。
页面组件(Page) #
page.js
文件都表示一个页面组件。当浏览器请求一个特定路径时,Next.js 会渲染与该路径对应的页面组件。
布局(Layout) #
布局在 Next.js 13 中变得更加灵活。通过在 app/
目录中定义 layout.js
文件,你可以创建局部或全局布局,适用于应用的不同部分。
- 全局布局:
app/layout.js
是默认的全局布局,所有页面都将继承它。 - 局部布局:你可以在任意子目录中定义
layout.js
文件,这些布局将只应用于该目录及其子目录中的页面。
特殊页面 #
Next.js 13 支持几种特殊页面,用于处理应用中的特定情况:
error.js
:用于渲染全局错误页面。loading.js
:用于处理页面加载状态,通常与Suspense
组件结合使用。not-found.js
:用于渲染 404 页面,当路径找不到匹配页面时显示。
示例:
// app/not-found.js
export default function NotFound() {
return <h1>页面未找到</h1>;
}
我们根据上面的结构修改之前初始化的项目:
接下来让 ChatGPT 帮我们开发一个博客首页
之后复制代码粘贴到app/page.js
下,然后运行 yarn dev
同理,我们再让 ChatGPT 写一个关于页面粘贴到 app/about/page.js
export default function About() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-50">
<h1 className="text-4xl font-bold mb-4">关于本博客</h1>
<p className="text-lg mb-6 max-w-3xl text-center">
欢迎来到我的博客!这里是一个分享编程知识、技术教程和个人见解的地方。
我希望通过这篇博客,能与更多热爱技术的人分享我的经验和学习旅程。
</p>
<h2 className="text-2xl font-semibold mb-2">我的背景</h2>
<p className="text-lg mb-4 max-w-3xl text-center">
我是一名全栈开发者,热衷于探索新技术并分享我的学习过程。
在这个博客中,你会找到关于前端开发、后端技术、UI设计和更多主题的文章。
</p>
<h2 className="text-2xl font-semibold mb-2">联系我</h2>
<p className="text-lg mb-6 max-w-3xl text-center">
如果你有任何问题或建议,欢迎通过社交媒体联系我。
</p>
<a
href="/"
className="px-6 py-2 text-white bg-blue-500 rounded hover:bg-blue-600 transition"
>
返回首页
</a>
</div>
);
}
接下来访问 http://localhost:3000/about,可以看到对应的效果:
上面两个例子即:
app/page.js
对应根路径/
。app/about/page.js
对应/about
路由。
动态路由 #
通过方括号 [param]
定义动态路由,允许根据 URL 动态渲染不同的页面内容。/app/blog/[id]/page.js
我们让 ChatGPT 生成一些博客数据,然后在/app/blog/[id]/
目录下创建 config.js
然后完成 app/blog/[id]/page.js
页面:
import blogPosts from "./config";
export default function BlogDetail({ params }) {
const { id } = params;
const blogPost = blogPosts.find((post) => post.id == id);
return (
<div className="max-w-2xl mx-auto p-6 bg-white shadow-md rounded-lg">
<h1 className="text-3xl font-bold mb-2">{blogPost.title}</h1>
<p className="text-gray-600 mb-4">
<span>作者: {blogPost.author}</span> | <span>{blogPost.date}</span>
</p>
<p className="text-lg mb-4">{blogPost.summary}</p>
<div className="prose">
<p>{blogPost.content}</p>
</div>
</div>
);
}
当访问 /blog/1
时,id
的值为 1,并返回对于的博客内容:
嵌套路由 #
Next.js 13 支持嵌套路由,通过定义 layout.js
文件,可以在不同页面之间共享布局。例如在 /dashboard
路由下加载多个页面,但这些页面共享相同的侧边栏或导航栏。
├── app
│ ├── dashboard
│ │ ├── analytics
│ │ │ └── page.js
│ │ ├── layout.js
│ │ └── settings
│ │ └── page.js
layout.js
文件用于定义全局布局,page.js
文件则是具体的页面内容。通过 children
属性,可以将嵌套页面放入布局中。
export default function DashboardLayout({ children }) {
return (
<div className="flex h-screen">
<div className="w-1/4 bg-gray-800 text-white p-4">
<h2 className="text-lg font-semibold">SideBar</h2>
<ul>
<li>
<a
href="/dashboard/analytics"
className="block py-2 hover:bg-gray-700"
>
analytics
</a>
</li>
<li>
<a
href="/dashboard/settings"
className="block py-2 hover:bg-gray-700"
>
settings
</a>
</li>
</ul>
</div>
<main className="flex-1">{children}</main>
</div>
);
}
children
会动态渲染 /dashboard/analytics
或 /dashboard/settings
的内容。
API 路由 #
Next.js 13 中的 API 路由与页面路由类似,放置在 app/api/
目录下的文件会自动映射为 API 端点。API 路由允许你在应用中轻松创建后端功能,而无需设置独立的服务器。
基本 API 路由 #
每个在 app/api/
目录下的文件都对应一个 API 端点。例如,app/api/user.js
将映射为 /api/user
。
API 路由文件结构:
├── app
│ ├── api
│ │ └── user
│ │ └── route.js
export async function GET(request) {
const user = { id: 1, name: "张三" };
return new Response(JSON.stringify(user));
}
动态 API 路由 #
与页面路由相似,API 路由也支持动态参数。例如:
├── app
│ ├── api
│ │ └── user
│ │ ├── [id]
│ │ │ └── route.js
export async function GET(request, { params }) {
const userId = params.id;
const user = { id: userId, name: `User ${userId}` };
return new Response(JSON.stringify(user));
}
该文件对应的路径是 /api/user/:id
,其中 :id
是动态参数。
支持的 HTTP 方法 #
API 路由可以处理不同的 HTTP 请求类型,比如 GET
和 POST
,每种请求类型可以有单独的处理函数。
// /app/api/hello/route.js
export async function GET(request) {
return new Response(JSON.stringify({ message: 'This is a GET request' }));
}
export async function POST(request) {
const data = await request.json();
return new Response(JSON.stringify({ message: 'This is a POST request', data }));
}
前端与 API 路由交互 #
在 Next.js 中,前端可以使用 fetch
请求 API 路由,从而实现数据交互。
export default function HomePage() {
async function fetchData() {
const res = await fetch('/api/hello');
const data = await res.json();
console.log(data);
}
return (
<div>
<button onClick={fetchData}>Fetch Data</button>
</div>
);
}
点击按钮时,前端会发送请求到 /api/hello
,并在控制台中打印返回的数据。
总结 #
Next.js 13 通过全新的 App Router
和 API 路由机制,为前后端开发提供了极大的便利。路由基于文件系统,简单直观,动态路由、嵌套路由和布局的使用让页面管理更为灵活。掌握 Next.js 13 的路由与 API 功能,可以大幅提升开发效率,并构建出结构清晰、性能优越的现代 Web 应用。
参考 #
https://nextjs.org/docs/app/building-your-application/routing#terminology