【Next.js 进阶】App Router的高级用法(下)

前言

先附上源码地址:next-ts-seed

之前介绍了Next.js App Router的基本使用:一文梳理Next.js 14的App Router,和包含平行路由的一些高级用法:【Next.js 进阶】App Router的高级用法(上),本篇继续讲解App Router的一些高级用法

对Next.js14项目搭建还不熟悉的,可参考我的另一篇文章:给上市公司从0到1搭建Next.js14项目

1. 拦截路由

拦截路由允许你在当前布局内加载应用其他部分的路由,通常与平行路由一同使用,以实现Modal对话框效果

例如图片中左边的路由为"/feed",当点击图片进行路由跳转到"/photo/123"时,浏览器上的路径被更改为"/photo/123",但/photo/123的内容以对话框Modal的形式展示在路由"/feed"上

而当在浏览器中直接输入路由"/photo/123"或刷新浏览器时,页面将直接呈现/photo/123的内容,而非以对话框Modal的形式呈现

在 Next.js 中,实现拦截路由需要你在命名文件夹的时候以 (..) 开头,规则为:

文件命名 匹配层级
(.) 表示匹配同一层级
(..) 表示匹配上一层级
(..)(..) 表示匹配上上层级
(...) 表示匹配根目录

但是要注意的是,这个匹配的是路由的层级而不是文件夹的层级,就比如路由组、平行路由这些不会影响 URL 的文件夹就不会被计算层级。

1. 新建app/food/photo/page.tsx

该页面为输入路由"/food/photo"或刷新浏览器时,页面呈现的内容

js 复制代码
export default function Page() {
  return <article>路由photo</article>;
}

2. 新建app/food/@modal/(.)photo/page.tsx

平行路由@modal不算url的层级,因此文件夹photo与文件夹(.)photo都在文件夹food下,属于同一层级,因此括号中使用一个点

js 复制代码
export default function Page() {
  return <div style={{ width: 500, height: 500, position: 'absolute', background: 'blue' }}>拦截路由photo</div>;
}

3. 新建app/food/layout.tsx

在layout中定义平行路由的插槽modal

js 复制代码
export default function Layout({ children, modal }: { children: React.ReactNode; modal: React.ReactNode }) {
  return (
    <section>
      {children}
      {modal}
    </section>
  );
}

4. 新建app/food/page.tsx

定义一个跳转到/photo的链接

js 复制代码
import Link from 'next/link';

export default function Page() {
  return (
    <article>
      <Link href="/food/photo">图片</Link>
    </article>
  );
}

5. 新建app/food/default.tsx

定义平行路由children插槽的默认文件

js 复制代码
export default function Default() {
  return null;
}

6. 新建app/food/@modal/default.tsx

定义平行路由modal插槽的默认文件

js 复制代码
export default function Default() {
  return null;
}

重启项目,访问路由/food,点击图片跳转到/food/photo,但页面显示的内容是@modal/(.)photo中的内容

刷新浏览器,此时路由为/food/photo,页面直接显示为"路由photo"

2. API 路由

API 路由在Next.js 14中又被称为Route Handlers(路由拦截器)。作为node中间层,可用来封装业务逻辑,减少客户端bundle体积;也可作为网关代理,隐藏真实后端接口

1. 基本使用

与page.tsx文件会自动映射为页面的路由一样,app目录下的route.ts文件会映射为一个api接口

为了避免route.ts与page.tsx冲突,可在app下新建api文件夹,专门用于存放API路由

1. 新建app/api/route.ts

js 复制代码
export async function GET() {
  return Response.json({ name: '张三' });
}

启动项目,例如我的端口号为3001,在浏览器中访问http://localhost:3001/api可以看到接口返回{ name: '张三' }

2. 修改app/page.tsx

在路由页面中使用API 路由

js 复制代码
async function getData() {
  const data = await fetch('http://localhost:3001/api');
  return data.json();
}

export default async function Home() {
  const data = await getData();
  return <article>{data.name}</article>;
}

访问http://localhost:3001,页面显示为张三

在项目中,可封装后台的业务逻辑

js 复制代码
export async function GET() {
  const res = await fetch('https://www.test.com/...');
  const data = await res.json();
  //处理数据...
  return Response.json({ data });
}

2. 动态API路由

动态API路由文件命名规则与路由文件一致

1. [folderName]

获取单个参数

新建app/api/[id]/route.ts

js 复制代码
export async function GET(request: Request, { params }: { params: { id: string } }) {
  return Response.json({ name: '张三', id: params.id });
}

访问http://localhost:3001/api/1,页面返回{"name":"张三","id":"1"}

2. [...folderName]

获取多个参数(至少得有一个参数)

新建app/api/[...key]/route.ts

js 复制代码
export async function GET(request: Request, { params }: { params: { key: string[] } }) {
  const key = params.key;
  return Response.json({ key });
}

访问http://localhost:3001/api/1/2,页面返回{"key":["1","2"]}

3. [[...folderName]]

获取多个参数(可一个参数都没有)

新建app/blog/[[...key]]/route.ts

js 复制代码
export async function GET(request: Request, { params }: { params: { key: string[] } }) {
  const key = params.key;
  return Response.json({ key });
}

访问http://localhost:3001/api/blog,页面返回一个空对象{}

3. 重定向

1. 客户端组件重定向

使用useRouter进行重定向

点击按钮,使用router.push进行路由跳转

js 复制代码
'use client';

import { useRouter } from 'next/navigation';

export default function Page() {
  const router = useRouter();

  return (
    <button type="button" onClick={() => router.push('/login')}>
      Dashboard
    </button>
  );
}

2. 服务端组件重定向

当接口请求失败时,使用redirect方法进行重定向

js 复制代码
import { redirect } from 'next/navigation';

async function fetchTeam(id) {
  const res = await fetch('https://...');
  if (!res.ok) return undefined;
  return res.json();
}

export default async function Profile({ params }) {
  const team = await fetchTeam(params.id);
  if (!team) {
    redirect('/login');
  }

  // ...
}

3. 中间件重定向

当cookie中没有验证信息时,使用NextResponse.redirect进行重定向

js 复制代码
import { NextResponse } from 'next/server';

import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token');
  if (token) {
    return NextResponse.next();
  } else {
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

export const config = {
  matcher: '/blog/:path*',
};

结尾

对Next.js感兴趣的,可先关注我,后续将继续更新相关内容

参考资料:

nextjs.org/docs/app/bu...

相关推荐
吃杠碰小鸡20 分钟前
commitlint校验git提交信息
前端
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇1 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒1 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐1 小时前
前端图像处理(一)
前端
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪2 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背2 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript