如果想看专栏的方式可以点击进入Next图册目实践
Next.js Router
大家好呀,这里是梦兽编程。今天分享的内容是Next.js
非常重要的内容。它关系到Next.js
的整个系统的运行。如果你有ThinkPHP3.2
的开发经验,那这个Next.js 13
的文件路由是非常容易理解的。它就是一个很典型的 MVC Web 开发架构。
本次的花瓣发现案例将以App Router 为例子。我们会在上次分享安装Next.js
得到的项目目录如下:
shell
├── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── next.config.js
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
│ ├── next.svg
│ └── vercel.svg
├── tailwind.config.ts
└── tsconfig.json
而我们的 app 项目路径就是Next.js 13
使用 App router 的文件目录。如果你想使用Page Router
。你可以改成page
文件夹。注意的page
文件的运行文件不是page.tsx
在app
文件夹下我们可以看到有
shell
├── app
│ ├── favicon.ico
│ ├── globals.css // 全局的css
│ ├── layout.tsx // 页面的layout布局
│ └── page.tsx // page 只要的内容
当然我们还可以可选一些可选的文件内容,一般来说我们用的比较多的是前面两个。如果你是需要自定义路由则可以添加route.tsx
文件进行定义。next.js
app router 中优先匹配route.tsx
路由规则。
ps:
page.tsx
和route.tsx
是不能共存的
-
layout Shared UI for a segment and its children
-
page Unique UI of a route and make routes publicly accessible
-
loading Loading UI for a segment and its children (类似 React 中的 Suspense 一样的用法)
-
not-found Not found UI for a segment and its children (没有匹配到路由的渲染页面可以理解成 404.html)
-
error Error UI for a segment and its children
-
global-error Global Error UI
-
route Server-side API endpoint
-
template Specialized re-rendered Layout UI (模版:可以理解成服务端渲染的组件,或者 C 语言中的宏,把公共的 HTML 渲染处理写在一起)
-
default Fallback UI for Parallel Routes (如果你有一些数据需要异步加载想做骨架子的时候可以用到它)
以上的机制在每一个"page"(页面)文件夹都可以写一份,如果不存在会读取父级
多级路由映射
有了这些基础概念以上的基本路由概念之后,我们就可以愉快的做一些"MVC"的 web 开发了。如果我们需要拥有和本次项目实践差不多路由https://huaban.com/discovery
。在Next.js
要如何实现呢?
1. 开发小技巧
如果你想偷懒快速生成代码可以安装ES7+React...
的插件
我们只需要在 app 文件下新增一个discovery
文件夹,并且新增page.tsx
文件即可。
//
├── discovery
│ └── page.tsx
├── favicon.ico
├── globals.css
├── layout.tsx
└── page.tsx
在浏览器输入 http://localhost:3000/discovery 即可看到我们进入"MVC" 模式下的 discovery 应用页面。
Next.js App Router 里的 Params 参数设置
1. 利用文件路由
在我们 React router 开发中经常会遇到这种路由http://localhost:3000/discovery/:id
。在Next.js 13
中应该如何实现这样的功能呢?
我们只需要在 discovery 文件夹下新增一个[id]文件夹目录同时新增page.tsx
即可达到目的。这时候的文件目录将会变成以下结构:
shell
├── [id]
│ └── page.tsx
└── page.tsx
我们如果需要获取params
参数应该如何获取呢?
ts
import React from "react";
function page({ params }: { params: { id: string } }) {
return <div>discovery Id is : {params.id}</div>;
}
export default page;
是的 mvc 架构就是这么简单粗暴,我们在浏览器中输入http://localhost:3000/discovery/rex
可以得到以下结果
2. 多个参数我们要如何应对?
在上面的例子中,我们知道传统 web mvc 架构的粗暴处理方式,为什么现在的 web 框架都是使用 router map 做正则配置呢?如果我们遇到这么一个路由http://localhost:3000/discovery/:id/:pid/:cid
,在这个路由中如果我们还是安装 mvc 文件路由的方式,我们和容易就写出多个文件夹嵌套文件路径。参数多了人都会奔溃!!!
当然在 next.js 文件路由中。还是友好的给我们提供了方案的。我们只需要修改我们的文件夹就好了,比如
shell
// app 文件夹下新增demo 并且 [...slug]
.
├── demo
│ ├── [...slug]
│ │ └── page.tsx
│ └── page.tsx
主要这里我们多个参数获取的时候需要修改一下一下 page 的获取。需要在 slug 的属性下获取
ts
import React from "react";
function page({ params }: { params: { slug: string[] } }) {
return <div>{params.slug.map((v) => v)}</div>;
}
export default page;
Next.js Rest Api 的处理
1. Rest Api 如何定义并响应
我们做服务端应用开发最基础的功能就是对业务进行增删改成,在 Next.js 中同样给我们提供一套 Rest Api 的机制。就是我们上面提到的router.tsx
文件。一般来说我们会在app/api
里面进行开发,因为上面有提及到route.(tsx|ts|jsx|js)
是不能与page.(tsx|jsx)
共存一个文件目录下的。
同样路由规格还是以 next.js 的 mvc 文件规则一样。如果我们需要开发一个http://localhost:3000/api/discovery
。我们需要把我们的项目结构改成:
shell
// app 文件夹下
.
├── api
│ └── discovery
│ └── route.ts
我们大致来看一下 route.ts 的大致模版
ts
// 分别对应 http 协议请求中的 get post put delete head 和 options 请求协议
export async function GET(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function DELTE(request: Request) {}
export async function HEAD(request: Request) {}
export async function OPTIONS(request: Request) {}
在服务端我们需要进行内容的响应也就是 HTTP 请求中的 Response,在 Next.js 中next/server
中提供 NextResponse 的静态类方法。它和 w3c 定义中的 Response 类型基本的实现。这里我们就以GET
请求例子,进行讲解。比如我们需要响应一个{code:0}
的 json 格式
ts
import { NextResponse } from "next/server";
export async function GET(request: Request) {
return NextResponse.json({ code: 0 }); // 默认http 响应状态码 为200
// 可通过第二参数进行设置,它是一个 ResponseInit 的参数类型
// return NextResponse.json({ code: 0 }, { status: 500 });
}
2. 如何在 Rest Api 中获取Query和Param的参数类型还有Post的数据类型
这里以POST
请求为例子,其他请求按需要获取
ts
// 项目结构
// .
// ├── api
// │ └── discovery
// │ ├── [id]
// │ │ └── route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request, params: { id: string }) {
// get query
const { searchParams } = new URL(request.url);
const allSearchParams = Object.fromEntries(searchParams);
// get params
const id = params.id;
// get formdata
const formdata = await request.formData();
const formdataJson = Object.fromEntries(formdata);
// get json 如果是 formdata 模式下不要请求
// const json = await request.json();
return NextResponse.json({
allSearchParams,
id,
formdataJson,
// json,
});
}
这个时候我们使用curl命令进行测试
txt
curl -X POST -F "name=rex" "http://localhost:3000/api/discovery/rex?a=111"
# response json content
{"allSearchParams":{"a":"111"},"formdataJson":{"name":"rex"}}
txt
curl -X POST -H "Content-Type: application/json" -d '{"name":"rex"}' "http://localhost:3000/api/discovery/rex?a=111"
{"allSearchParams":{"a":"111"},"json":{"name":"rex"}
3. 获取 cookeis 和 headers 操作
这里以PUT请求为例子
javascript
export async function PUT(request: NextRequest) {
const cookies = request.cookies.getAll()
// 不推荐 在构建类似 hexo 静态页面(SSG)的时候会报错,说你依赖了cookies无法构建
// import {cookies} from 'next/headers'
// const cookie = cookies()
const headers = request.headers;
const ContentType = headers.get('Content-Type')
}
服务端的页面跳转 redirect 在中间件里面用的比较多
ts
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
redirect('https://nextjs.org/')
}
Middleware 中间件拦截器
Middleware 是我们服务端web开发里面非常重要的一个功能指标之一,你可以了解成Axios
中的拦截器。在我们的开发过程中可以有一个地方进行处理。比如你的web应用需要进行Auth的校验可以使用Middleware进行一个入口全站应用,再比如你想每一个请求都进行一下日志打印的操作,这个时候中间件的出现真的是一杯美酒。
当然这个概念在其他web开发框架也有比如java spring boot
,php laravel
,node.js nest.js
,golang gin
。知道一个应用场景几乎其他语言的应用场景都会掌握只是语法上的区别和极端环境下的的问题了。
1. 我们在next.js中要如何使用Middleware
shell
# 在app文件
.
...
├── ....
├── middleware.ts (添加)
中间件的具体内容
ts
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";
// This function can be marked `async` if using `await` inside
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 比如我们对/api不做任何处理
if (!["/api"].some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next();
}
// 比如我们对 /demo 进行一下操作,如果没有权限的时候跳转到百度
if (["/demo"].some((prefix) => pathname.startsWith(prefix))) {
// handle someing
return NextResponse.redirect("http://www.baidu.com");
}
// 比如我们需要做一些日志操作的时候
// if (request.method === "POST") {
// console.log(await request.json());
// }
return NextResponse.next();
}
// See "Matching Paths" below to learn more
// 匹配的路由机制 除非你业务非常简单,要不不推荐使用
// export const config = {
// matcher: '/:path*',
// }
结语
这里是梦兽编程,本次的代码更新将会放在Github本次项目的Github连接中。
我的B站视频号更多视频动态。
以上就是Next.js路由的所有内容了,如果对你的学习有任何帮助记得给梦兽编程一个一件三连。