首页 > 其他分享 >Next.js 14 路由进阶:从约定式到动态路由的最佳实践

Next.js 14 路由进阶:从约定式到动态路由的最佳实践

时间:2024-12-24 12:32:06浏览次数:13  
标签:return 进阶 js params tsx export page 路由

在 Next.js 14 中,路由系统是最核心的功能之一。App Router 不仅带来了更好的性能,还提供了更灵活的路由组织方式。今天,我们就来深入探讨 Next.js 14 的路由系统。

路由组织结构

1. 基础约定

Next.js 14 的路由基于文件系统,每个文件夹代表一个路由段:

app/
├── page.tsx           # 首页 (/)
├── about/
│   └── page.tsx      # 关于页面 (/about)
├── blog/
│   ├── page.tsx      # 博客列表页 (/blog)
│   └── [slug]/       # 动态路由
│       └── page.tsx  # 博客详情页 (/blog/post-1)
└── (marketing)/      # 路由组
    ├── pricing/
    │   └── page.tsx  # 价格页面 (/pricing)
    └── contact/
        └── page.tsx  # 联系页面 (/contact)

2. 路由组织最佳实践

// 使用路由组进行功能分组
app/
├── (auth)/              # 认证相关路由
│   ├── login/
│   │   └── page.tsx
│   └── register/
       └── page.tsx
├── (dashboard)/         # 仪表板相关路由
│   ├── layout.tsx      # 仪表板共享布局
│   ├── overview/
│   │   └── page.tsx
│   └── settings/
│       └── page.tsx
└── (public)/           # 公开页面
    ├── layout.tsx
    └── page.tsx

动态路由设计

1. 单一动态段

// app/users/[id]/page.tsx
export default async function UserProfile({ 
  params 
}: { 
  params: { id: string } 
}) {
  const user = await fetchUser(params.id);

  return (
    <div className="user-profile">
      <h1>{user.name}</h1>
      <div className="user-details">
        {/* 用户详情 */}
      </div>
    </div>
  );
}

// 生成静态路由参数
export async function generateStaticParams() {
  const users = await fetchUsers();

  return users.map((user) => ({
    id: user.id.toString(),
  }));
}

2. 多段动态路由

// app/[category]/[subCategory]/[productId]/page.tsx
interface ProductPageProps {
  params: {
    category: string;
    subCategory: string;
    productId: string;
  };
}

export default async function ProductPage({ params }: ProductPageProps) {
  const { category, subCategory, productId } = params;

  const product = await fetchProduct({
    category,
    subCategory,
    productId,
  });

  return (
    <div className="product-page">
      <Breadcrumb
        items={[category, subCategory, product.name]}
      />
      <ProductDetails product={product} />
    </div>
  );
}

3. 捕获所有路由

// app/docs/[...slug]/page.tsx
export default async function DocPage({ 
  params 
}: { 
  params: { slug: string[] } 
}) {
  // slug 是一个数组,包含所有路径段
  const path = params.slug.join('/');
  const doc = await fetchDoc(path);

  return (
    <div className="doc-content">
      <DocRenderer content={doc.content} />
    </div>
  );
}

路由拦截与中间件

1. 路由中间件

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 获取当前路径
  const path = request.nextUrl.pathname;

  // 检查认证状态
  const isAuthenticated = request.cookies.has('auth-token');

  // 保护的路由
  if (path.startsWith('/dashboard') && !isAuthenticated) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 添加自定义请求头
  const response = NextResponse.next();
  response.headers.set('x-custom-header', 'my-value');

  return response;
}

// 配置中间件匹配路径
export const config = {
  matcher: [
    '/dashboard/:path*',
    '/api/:path*',
  ],
};

2. 路由拦截

// app/posts/[id]/page.tsx
import { headers } from 'next/headers';

export default async function Post({ params }: { params: { id: string } }) {
  const headersList = headers();
  const referer = headersList.get('referer');

  // 检查是否从允许的来源访问
  if (referer && !isAllowedReferer(referer)) {
    redirect('/unauthorized');
  }

  const post = await fetchPost(params.id);
  return <PostContent post={post} />;
}

平行路由和拦截路由

1. 平行路由

// app/layout.tsx
export default function Layout({
  children,
  auth,
  modal,
}: {
  children: React.ReactNode;
  auth: React.ReactNode;
  modal: React.ReactNode;
}) {
  return (
    <div className="layout">
      {children}
      {auth}
      {modal}
    </div>
  );
}

// app/@auth/login/page.tsx
export default function LoginModal() {
  return (
    <div className="modal">
      <LoginForm />
    </div>
  );
}

// app/@modal/photo/[id]/page.tsx
export default function PhotoModal({
  params,
}: {
  params: { id: string };
}) {
  return (
    <div className="modal">
      <PhotoViewer id={params.id} />
    </div>
  );
}

2. 路由拦截模式

// app/feed/page.tsx
import { Feed } from '@/components/Feed';

export default function FeedPage() {
  return <Feed />;
}

// app/feed/(..)photo/[id]/page.tsx
export default function InterceptedPhotoModal({
  params,
}: {
  params: { id: string };
}) {
  return (
    <div className="modal">
      <PhotoViewer id={params.id} />
    </div>
  );
}

路由加载状态

1. 加载界面

// app/posts/loading.tsx
export default function Loading() {
  return (
    <div className="loading-container">
      <div className="loading-skeleton">
        <div className="skeleton-header" />
        <div className="skeleton-content" />
      </div>
    </div>
  );
}

2. 流式渲染

// app/dashboard/page.tsx
import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <div className="dashboard">
      <Suspense fallback={<StatsSkeleton />}>
        <Stats />
      </Suspense>

      <Suspense fallback={<ChartsSkeleton />}>
        <Charts />
      </Suspense>

      <Suspense fallback={<TableSkeleton />}>
        <Table />
      </Suspense>
    </div>
  );
}

路由缓存策略

1. 路由段缓存

// app/products/[id]/page.tsx
export const revalidate = 3600; // 1小时后重新验证

export default async function ProductPage({
  params,
}: {
  params: { id: string };
}) {
  const product = await fetchProduct(params.id);

  return (
    <div className="product">
      <h1>{product.name}</h1>
      <ProductDetails product={product} />
    </div>
  );
}

2. 按需重新验证

// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';

export async function POST(request: NextRequest) {
  const { path, tag } = await request.json();

  if (path) {
    revalidatePath(path);
  }

  if (tag) {
    revalidateTag(tag);
  }

  return Response.json({ revalidated: true });
}

高级路由技巧

1. 条件路由

// app/[lang]/layout.tsx
export default async function LocaleLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  const messages = await loadMessages(lang);

  return (
    <html lang={lang}>
      <body>
        <LocaleProvider messages={messages}>
          {children}
        </LocaleProvider>
      </body>
    </html>
  );
}

// 生成静态路由参数
export async function generateStaticParams() {
  return [
    { lang: 'en' },
    { lang: 'zh' },
    { lang: 'ja' },
  ];
}

2. 路由组合

// app/(shop)/products/[category]/layout.tsx
export default function ShopLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { category: string };
}) {
  return (
    <div className="shop-layout">
      <aside>
        <CategoryNav category={params.category} />
      </aside>
      <main>{children}</main>
    </div>
  );
}

性能优化建议

  1. 路由预加载
    import { useRouter } from 'next/navigation';
    

function NavLink({ href, children }) { const router = useRouter();

return ( <a href={href} onm ouseEnter={() => router.prefetch(href)} > {children} ); }


2. **选择性缓存**:
```typescript
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    next: {
      tags: ['data'],      // 使用标签进行缓存
      revalidate: 3600,    // 缓存时间
    },
  });

  return res.json();
}

写在最后

Next.js 14 的路由系统提供了强大的功能和灵活的配置选项。合理使用这些特性,可以构建出性能优秀、用户体验良好的应用。记住以下几点:

  1. 合理组织路由结构,使用路由组进行功能分组
  2. 善用动态路由和路由拦截
  3. 适当配置缓存策略
  4. 注意性能优化
  5. 使用中间件增强路由功能

在下一篇文章中,我们将深入探讨 Next.js 14 的数据获取和状态管理。如果你有任何问题或建议,欢迎在评论区讨论!

如果觉得这篇文章对你有帮助,别忘了点个赞

标签:return,进阶,js,params,tsx,export,page,路由
From: https://blog.csdn.net/ChengFengTech/article/details/144688065

相关文章

  • node.js毕设 点石家装服务平台 论文+程序
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于家装服务平台的研究,现有研究主要以综合性家装服务为主,专门针对像点石家装这样具有特定品牌的家装服务平台的研究较少。在国内外,家装服务行业竞争激......
  • node.js毕设 大学班级管理系统 论文+程序
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于大学班级管理系统的研究,现有研究主要集中在学校整体管理系统的构建,专门针对大学班级管理系统这一细分领域的研究较少。在国内外的教育管理研究中,虽......
  • 记一次百度地图JSAPI的使用
    百度开放平台百度地图开放平台注册登录百度账号进入控制台创建应用,复制你的key(自己找,就在网页上,找不到就转行吧)页面代码引入百度地图JS代码<scripttype="text/javascript"src="https://api.map.baidu.com/api?v=3.0&ak=你的key"></script>在哪引都没事,无论你是Vue项......
  • 学习threejs,PerspectiveCamera透视相机和OrthographicCamera正交相机对比
    ......
  • 基于React+Nextjs+Nodejs开发的web3入门项目
    这是一个学习Web3技术的练习项目。oneNFS是一个音乐创作Web3平台。我们利用区块链技术和先进的加密技术,为艺术家和听众创建一个公平、透明、以用户为中心的生态系统。源码地址:https://github.com/geeeeeeeek/oneNFS演示地址:https://one-nfs.vercel.app/主要功能......
  • Java 项目实战:全方位解析基于 Spring Boot、MySQL、FastJSON、MyBatis - Plus、Swagge
    1.引言1.1编写目的本设计文档详细阐述了SNS系统的架构、功能模块、数据结构、接口设计以及系统部署等方面,为系统的开发、测试、维护提供全面的指导,确保项目团队成员对系统有清晰一致的理解,保证系统的顺利实施与迭代优化。1.2适用范围本设计文档适用于SNS系统的开发团队、测试......
  • js中有哪些类型的弹出框?
    在JavaScript和前端开发中,有几种常见的弹出框(或称为对话框)类型。这些弹出框可以帮助你与用户进行交互,提供信息,或者请求输入。以下是一些主要的类型:警告框(Alert):alert()函数是JavaScript中最简单的弹出框类型。它显示一个带有指定消息和OK按钮的警告框。例如:alert("这是一个警......
  • 路由
    路由参考地址:https://router.vuejs.org/zh/​ Vue实质上是一个SPA(单Web应用程序)应用,也就是实质上其实只有一张页面。所以我们使用route进行切换。基本构成:​ 1、导航区、展示区​ 2、路由器​ 3、制定路由的具体规则(什么路径,对应着什么组件)​ 4、形成一个一个的......
  • Java 项目实战:基于 Spring Boot、MyBatis、PageHelper、Spring Security、FastJSON、S
    一、系统概述1.1系统目标本系统的主要目标是提供一个集成化的商品管理平台,实现以下功能:高效的商品信息管理,包括商品的基本信息、类型、供应商、客户等的录入、查询、修改和删除。精确的采购流程管理,涵盖采购订单的创建、修改、查询、入库操作以及与供应商的信息关联。完善......
  • NodeJS社区独居老人健康管理系统-毕业设计源码21353
    NodeJS社区独居老人健康管理系统摘  要随着科技的进步,信息技术已经深刻地影响了社会的各个领域。计算机的普及已经成为了不可或缺的一部分,它为我们提供了无限的可能性。该社区独居老人健康管理系统提供了多种功能,包括查看首页、个人中心、网站公告、健康资讯、医生信息、......