前言
先附上源码地址: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感兴趣的,可先关注我,后续将继续更新相关内容
参考资料: