浙江省建设厅官方网站信用平台,网站建设管理工作总结报告,企业怎么做推广,wordpress右键插件NextJs 初级篇 - 安装 | 路由 | 中间件 一. NextJs 的安装二. 路由2.1 路由和页面的定义2.2 布局的定义和使用2.3 模板的定义和使用① 模板 VS 布局② 什么是 use client 2.4 路由跳转的方式2.5 动态路由2.6 路由处理程序① GET 请求的默认缓存机制② 控制缓存或者退出缓存的手… NextJs 初级篇 - 安装 | 路由 | 中间件 一. NextJs 的安装二. 路由2.1 路由和页面的定义2.2 布局的定义和使用2.3 模板的定义和使用① 模板 VS 布局② 什么是 use client 2.4 路由跳转的方式2.5 动态路由2.6 路由处理程序① GET 请求的默认缓存机制② 控制缓存或者退出缓存的手段③ 控制缓存的时效 revalidate④ 常见的编写问题 3. 中间件 一. NextJs 的安装
首先NextJs要想使用node版本不能太低最低也要18以上版本不够的可以用nvm来管理Mac用户的安装可以参考这篇文章 mac 系统正确安装nvm
版本满足的同学建议直接使用脚手架来创建nextJs输入脚手架命令这里又切了阿里的镜像以防万一关闭SSL认证
npm config set registry https://registry.npm.taobao.org
npm config set strict-ssl false
npx create-next-applatest结果如下这里我们优先使用App Router后面会讲解 生成结果如下 启动项目 npm run dev之后访问http://localhost:3000/ 即可
二. 路由
NextJs 拥有两套路由两者兼容
Pages Router在pages目录下创建对应的文件或者目录即是一个路由。App RouterNextJs从版本13.4起的默认路由模式
为什么官方在新版本中默认的路由模式采用了App呢
pages 下每个文件都会被当成路由不符合开发习惯app 架构新增了布局layout、模版template、加载状态loading、错误处理error、404 等文件为项目开发提供了一套规范。
2.1 路由和页面的定义
我们这里主要讲官方更推荐的AppRouter如图 这里简单介绍下
我们约定使用 page 来代表一个页面就好比React中使用index。和React一样默认导出个组件即可。文件的路径就是对应的路由。app/page.tsx 对应路由 /app/about/page.tsx 对应路由 /aboutapp/address/page.tsx 对应路由 /address后缀名可以使用.js、.jsx、.tsx
2.2 布局的定义和使用
首先布局组件有这么几个特征
定义一个布局需要新建一个名称为layout 的固定文件该组件接收一个children代表子页面或者子布局。布局可以嵌套父布局中有一个子布局。
根布局还有额外几个特征
定义在app/layout.tsx文件中会应用于所有的路由。并且此文件必须存在。根布局文件中必须包含html和body标签。同时其他布局不能包含这些标签。
根布局文件内容示例
import { Inter } from next/font/google;
import ./globals.css;
const inter Inter({ subsets: [latin] });
export default function RootLayout({children,
}: Readonly{children: React.ReactNode;
}) {return (html langenbody className{inter.className}{children}/body/html);
}布局的嵌套: address 下有个子目录home每个文件都有自己的layout布局。 AddressLayout
const AddressLayout ({ children }: any) {return (nav我是Address的Layout/nav{children}/)
}
export default AddressLayoutHomeLayout
const HomeLayout ({ children }: any) {return (nav我是Home的Layout/nav{children}/)
}
export default HomeLayout访问http://localhost:3000/address/home呈现效果
2.3 模板的定义和使用
模板跟布局的用法是一样的例如我们在address下定义一个固定名称的模板文件template.tsx
const AddressTemplate ({ children }: any) {return (nav我是Address的模板/nav{children}/)
}
export default AddressTemplate此时我们访问 http://localhost:3000/address/home呈现效果 这说明了layout 会包裹 templatetemplate 再包裹 page 。
那模板和布局之间存在着什么差异
① 模板 VS 布局
状态的维持
模板路由切换的时候模板的内容会随之变更。布局路由切换的时候布局的内容不会更改。
框架默认行为的更改以组件Suspense为例这个组件就是一个渲染fallback组件效果的功能。
模板模板内使用Suspense每次切换路由的时候都会展示fallback组件。布局模板内使用Suspense每次切换路由的时候只会展示一次。
其实这两个点比较类似说白了就是每次在切换路由的时候使用 template 就可以重新触发渲染。
来个案例目录结构如下 sonA
const SonA () {return 我是SonA/
}
export default SonAsonB
const SonB () {return 我是SonB/
}
export default SonB父组件page.tsx
import Link from next/link;const Parent () {return nav当前页面是父组件/navnav跳转到Link href/parent/sonASonA/Link/navnav跳转到Link href/parent/sonBSonB/Link/nav/
}export default Parenttemplate.tsx
use client
import { useState } from react
const AddressTemplate ({ children }: any) {const [count, setCount] useStatenumber(0);return (h1Template点击次数: {count}/h1button onClick{() setCount(count 1)}点击/button{children}/)
}
export default AddressTemplatelayout.tsx
use client
import { useState } from react
const ParentLayout ({ children }: any) {const [count, setCount] useStatenumber(0);return (h1Layout点击次数: {count}/h1button onClick{() setCount(count 1)}点击/button{children}/)
}
export default ParentLayout结果如下 ② 什么是 use client
use client 是一个指令放置在文件的顶部通常是模块的第一行。它告诉 Nextjs 该模块应该在客户端环境中执行而不是在服务器端。
备注默认情况下NextJs中的页面是由服务端渲染的即SSR。
2.4 路由跳转的方式
在上述案例中我们使用 Link 组件来完成路由的跳转。而NextJs中一共支持3种路由跳转的方式
Link 组件。使用钩子函数useRouter使用 redirect 函数
第一种 Link 组件是最为推荐的方式例如上述案例中的代码。
Link href/parent/sonASonA/Link除此之外还可以更改默认的跳转行为App Router 的默认行为是滚动到新路由的顶部。可以通过传参 scroll 为false改变。
Link href/parent/sonA scroll{false}SonA/Link第二种使用useRouter函数
use client
import Link from next/link;
import { useRouter } from next/navigationconst Parent () {const router useRouter();return nav当前页面是父组件/navnav跳转到Link href/parent/sonASonA/Link/navnav跳转到button onClick{() router.push(/parent/sonB)}SonB/button/nav/
}export default Parent我们还能发现一旦使用钩子函数这种客户端触发的动作就需要加上标识use client代表它是需要客户端渲染。 第三种redirect用于给服务端组件用的伪代码如下
import { redirect } from next/navigationasync function login(id) {const res await login()if (!res.ok) {redirect(/error)}return res.json()
}2.5 动态路由
假设我们访问某个订单详情页订单号可能是动态会变的这里假设订单号是123456一般我们可以设计两种URL
/orderInfo/123456/orderInfo?orderId123456
先说第一种就需要通过动态路由的方式来访问。NextJs中需要将文件夹的名字用方括号[] 扩住。例如我们创建一个目录orderInfo/[orderId]代表orderId是一个动态路由其中这个动态参数可以通过orderId字段来读取。 然后页面上我们希望把订单号展示出来。其中组件接收一个参数params如下
import React from react
const About ({ params }: any) {return h1订单详情页面/h1span{params.orderId}/span/
}
export default About那么在访问 http://localhost:3000/orderInfo/123 的时候效果如下 那如果我们动态参数后还跟着路由例如 http://localhost:3000/orderInfo/123/shop 若直接访问我们看看结果 我们发现此时404了可见这种情况下动态参数[orderId]只会接收第一个路由片段那怎么办呢我们可以将[orderId] 改为: [[..orderId]]表示捕获所有后面所有的路由片段。 例如
2.6 路由处理程序
NextJs中对于客户端和服务端之间的API交互叫做路由处理程序。
编写一个简单的路由处理程序规则如下
接口的路径跟路由比较相似对应文件目录下创建一个名为 route 的文件即可。文件中需要导出固定名称的函数无需使用default exportGET、POST等。可以使用NextResponse代表返回的数据。
// route.js
export async function GET(request) {}
export async function HEAD(request) {}
export async function POST(request) {}
export async function PUT(request) {}
export async function DELETE(request) {}
export async function PATCH(request) {}
// 如果 OPTIONS 没有定义, Next.js 会自动实现 OPTIONS
export async function OPTIONS(request) {}一个简单的GET和POST请求 GET请求
import { NextRequest, NextResponse } from next/serverexport async function GET() {const data [{ id: 1, name: LJJ }]return NextResponse.json({ data })
}对应结果 POST请求
import { NextRequest, NextResponse } from next/server
export async function POST(request: NextRequest) {const data await request.json();return NextResponse.json(data)
}对应结果
① GET 请求的默认缓存机制
默认情况下使用NextResponse对象的GET请求会被NextJs缓存起来。本地模式下不会缓存生产模式则会
我们改一下我们的getUserList接口
import { NextResponse } from next/serverexport async function GET() {const data [{ id: 1, name: LJJ, date: new Date().getMilliseconds() }]return NextResponse.json({ data })
}然后跑命令 npm run build npm run start 我们能发现构建产物/api/getUserList 是静态的。 我们请求接口发现无论请求几遍时间戳永远不会变是因为被缓存了。
② 控制缓存或者退出缓存的手段
退出缓存的方式有这么几种
GET 请求中使用了 Request 对象。例如返回对象依赖于Request的参数。添加其他 HTTP 方法比如同一个route文件中同时定义了GET和POST函数。使用像 cookies、headers 这样的动态函数。也可以手动声明接口为动态模式。 文件顶端增加export const dynamic force-dynamic 即可
// 返回结果依赖于request的参数
export async function GET(request: NextRequest) {const searchParams request.nextUrl.searchParamsconst data [{ id: 1, name: searchParams.toString(), date: new Date().getMilliseconds() }]return NextResponse.json({ data })
}
// 使用cookies这种动态函数
export async function GET(request) {const token request.cookies.get(token)return Response.json({ data: new Date().toLocaleTimeString() })
}③ 控制缓存的时效 revalidate
我们可以在route文件的顶部增加声明例如以下就是标识缓存的有效期为10秒。
export const revalidate 10④ 常见的编写问题
首先是获取URL上的参数、获取Header、获取Request请求体JSON、Cookie等
import { NextRequest, NextResponse } from next/server
export async function POST(request: NextRequest) {// 获取URL上的参数const searchParams request.nextUrl.searchParamsconsole.log(searchParams.get(orderId));// 处理 Headerconst headersList new Headers(request.headers)const referer headersList.get(myHeader)console.log(referer)// 获取Request请求体 JSON)const data await request.json();// 如果是FormData// const formData await request.formData()// const name formData.get(name)return NextResponse.json(data)
}Postman请求http://localhost:3000/api/getDetail?orderId123 结果如下 重定向使用redirect即可。
import { redirect } from next/navigationexport async function POST(request: NextRequest) {redirect(https://www.baidu.com)
}获取Cookie、并设置到Response中则需要返回一个使用 Set-Cookie header 的 Response 实例
import { cookies } from next/headersexport async function POST() {const cookieStore cookies()const token cookieStore.get(test)console.log(token)return new Response(OK, {status: 200,headers: { Set-Cookie: myNewToken${12312312312} },})
}请求后
3. 中间件
NextJs 中中间件的定义可以在根目录中定义一个固定名称的文件middleware.ts它的内容有两个部分组成
// middleware.js
import { NextResponse } from next/server// 第一部分导出固定名称的中间件
export function middleware(request) {// ... 中间件逻辑
}
// 第二部分设置匹配路径
export const config {matcher: /api/:path*,
}这里直接给个案例假设我中间件有多个功能
中间件 headers处理相关的Header中间件 logging记录相关的日志
我们可以在根目录下创建个专门编写中间件的文件夹middlewares 两个中间件内容
// logging.ts
import { NextRequest } from next/server;
export const logging (next: any) {return async (request: NextRequest) {// ...console.log(logging)return next(request);};
};
// headers.ts
import { NextRequest } from next/server;
export const headers (next: any) {return async (request: NextRequest) {console.log(headers)return next(request);};
};然后我们可以设置下别名tsconfig.json 文件中 这样就可以在真正设置中间件的地方根路径的middleware.ts中进行设置
import { logging } from /middlewares/logging;
import { headers } from /middlewares/headers;
import { NextResponse } from next/server;function chain(functions: any, index 0) {const current functions[index];if (current) {const next: any chain(functions, index 1);return current(next);}return () NextResponse.next();
}export default chain([logging, headers]);export const config {matcher: /api/:path*,
}这样访问任何一个接口都会走相关的逻辑了。