技术篇:Next.js 路由与页面管理

·669 字·4 分钟
独立开发 Next.js
Next.js实战教程 - 系列文章
Part 6: 当前文章

学习目标:可以用 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

image.png

页面 #

路由与文件结构 #

Next.js 13 的路由系统依赖于文件系统的结构,这意味着你的应用的 URL 结构与 app/ 目录中的文件和文件夹结构直接相关。先看一个最直观的例子:

下图是初始化项目后的默认结构,app/page.js就是我们项目默认的首页。

image <em>1</em>.png

我们直接运行上面初始化的项目:yarn dev

image <em>2</em>.png

可以看到显示的即 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>;
}

我们根据上面的结构修改之前初始化的项目:

image <em>3</em>.png

接下来让 ChatGPT 帮我们开发一个博客首页

image <em>4</em>.png

之后复制代码粘贴到app/page.js下,然后运行 yarn dev

image <em>5</em>.png

同理,我们再让 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,可以看到对应的效果:

image <em>6</em>.png

上面两个例子即:

  • app/page.js 对应根路径 /
  • app/about/page.js 对应 /about 路由。

动态路由 #

通过方括号 [param] 定义动态路由,允许根据 URL 动态渲染不同的页面内容。/app/blog/[id]/page.js

我们让 ChatGPT 生成一些博客数据,然后在/app/blog/[id]/目录下创建 config.js

image <em>7</em>.png

然后完成 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,并返回对于的博客内容:

image <em>8</em>.png

嵌套路由 #

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 的内容。

image <em>9</em>.png

image <em>10</em>.png

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));
}

image <em>11</em>.png

动态 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 是动态参数。

image <em>12</em>.png

支持的 HTTP 方法 #

API 路由可以处理不同的 HTTP 请求类型,比如 GETPOST,每种请求类型可以有单独的处理函数。

// /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

Next.js实战教程 - 系列文章
Part 6: 当前文章