nextjs进阶知识
这里我们开始讲解nextjs的进阶知识,这里需要你先提前掌握好基础知识的一些理解。同样的我们也尽可能做更多的案例以便你可以更好的学习这门语言。
路由篇
一 :并行路由
并行路由是一种高级路由机制,让我们在同一个布局中同时渲染多个页面来尝试一下。这里的场景为Web应用程序构建一个复杂的仪表盘。以下为我们假设的一种页面结构,传统的方式为创建三个单独独立的布局,当然这里我们并不会采用这种方式,而是利用我们的插槽。

这里我们开始创建页面结构能够让你更好的去实现同样的功能。这里我们再创建一个complex-dashboard.tsx文件,这里我们可以先简单看一下代码结构以及示例,



这里我们该谈论如何在X shg中实现并行路由和X是插槽的定义而插槽有助我们模块化的方式组织内容,而创建一个插槽我们用文件夹定义为@<j文件名>,而定义的插槽会自动成为其相应布局中的道具,同样的我们先来看一下所要实现的结构,以便你更好的理解

这里我们开始展示我们的目录结构,并行路由要求我们的段同在于一个父类下,同时我们插槽的功能实现是依据与根路由,而在@notifications,@revenue,@Users下文件默认导出名为通知的反应组件,该返回值是一个带有di标签,


这里我们需要用到Card的组件,在src下创建components/card,我们需要将@notifications,@revenue,@Users的反应组件引入Card样式,这里我们为你提供了目录,以及card代码部分

card-代码部分
export const Card =({children}:{children:React.ReactNode})=>{
const cardStyle = {
padding:"100px",
margin:"10px",
boxShadow:"0 4px 8px rgba(0,0,0,0.2)",
border:"1px solid #ddd",
display:"flex",
justifyContent:"center",
alignItems:"center",
};
return <div style={cardStyle}>{children}</div>;
}
同样的你可以将@notifications,@revenue,@Users的反应组件中的di标签更换为Card并在顶部引入。这里我们做一个演示,一便你可以更好的理解我们目前的一些插槽的准备,这里我们以complex-dashboard为例
,同样的你可以需要接下来改动剩余的三个页面
接下来我们开始使用插槽,tsx文件的好处是它们自动作为道具传递给我们,布局组件不需要再进行导入。这里我们为你准备了layout.tsx相应的代码
layout.tsx代码
export default function ComplexDashboardLayout({
children,
users,
revenue,
notifications
}: {
children: React.ReactNode;
users: React.ReactNode;
revenue: React.ReactNode;
notifications: React.ReactNode;
}) {
return (
<div>
<div>{children}</div>
<div style={{ display: "flex", }}>
<div style={{display:"flex",flexDirection:"column"}}>
<div>{users}</div>
<div>{revenue}</div>
</div>
<div style={{display:"flex",flex:1}}>{notifications}</div>
</div>
</div>
);
}
这里我们可以看一下页面的效果,以便你更好的理解,

二:不匹配的路由
让我们深入了解路由的另一个关键方面,即处理不匹配的路由,例如在我们的仪表中,我们可以在默认的通知列表,和存档的通知列表进行导航

同样的让我们开始实现这个功能,在@ notificaitons中实现链接到archived文件夹,并创建archivedi文件夹包含在notification中,同样的archive下的page同样实现链接效果, 这里我们看一下层级目录

而对应的archived下的page.tsx内容为
import { Card } from "@/components/card";
import Link from "next/link"
export default function ArchivedNotificationsPage() {
return<Card>
<div> Archived Notifications</div>
<div>
<Link href="/complex-dashboard/">Default</Link>
</div>
</Card>
}
这里我们可以看一下页面效果,可以发现在点击archivedh时候,会相应的变化为Default在。这就是不匹配路由的作用,可以看到我们的通知,用户视图与此形成了完美的配合。默认情况下,在插槽中呈现各自的内容
,当你访问complex-dashboard时,仪表已有存档。当你点击时候会发现,它只会作用在当前的插槽。同样的我们可以借此满足很多效果,
三:条件路由
假设你想通过不同的用户来显示不同的内容,你可能会想为经过身份验证的用户提供一个仪表盘,为没有通过验证的客户提供一个登录页面,让我们来实现这个页面,同时存在于同一个ui,这里我们需要在complex-dashboard中创建登录页面,同时引用Card 这里我们不妨看一下代码,

同样的利用之间我们所运用的插槽,将其放入到complex-dashboard文件的layout.tsx中,这里我们引入login页面,并设置返回值,将当前isLoggedIn的值设置为false,并在底部输出login,这里我们已经为你准备好了代码
layout.tsx
export default function ComplexDashboardLayout({
children,
users,
revenue,
notifications,
login
}: {
children: React.ReactNode;
users: React.ReactNode;
revenue: React.ReactNode;
notifications: React.ReactNode;
login: React.ReactNode;
}) {
const isLoggedIn = false; // Replace with actual authentication logic
return isLoggedIn ? (
<div>
<div>{children}</div>
<div style={{ display: "flex", }}>
<div style={{display:"flex",flexDirection:"column"}}>
<div>{users}</div>
<div>{revenue}</div>
</div>
<div style={{display:"flex",flex:1}}>{notifications}</div>
</div>
</div>
):(
login
)
}
这里我们可以看一下页面的效果,当你将值更改为false时,会出现登录提示页面。同样的更改为ture会出现我们的仪表盘

四:拦截路由
现在让我们深入了解拦截路由,让我们分两部分来解决这个问题,首先我们先了解核心概念和约定。然后我们将看到如何使用拦截,路由是一种高级路由机制,它允许你在当前布局中从应用程序的另一端加载路由,当你想要展示新的内容,同时让用户保持相同的上下文,它特别有用,这里我们演示一个例子,例如你可以显示一个模型,但同样的它包含登录的页面,next设置来一些约定,让我们来实现这个功能

首先我们依旧是看一下我们的层级目录,这里会让你感觉有一些乱,所以这里我做了一个比较简单的案例,如果你想要更详细的了解拦截路由,这里我们为你准备了官网https://nextjs.org/docs
好的让我们接下来看一下目录 ,这里我们在app目录下创建f1,并在f1下创建f2,f3。这里我们需要知道nextjs对于拦截路由的文件定义为(.)<文件名>,因此我们将f2f3设置为拦截路由文件。同样的默认导出di
标签,并在f1n文件下创建f2f3链接

这里为了更好的区分页面,我们将f2导出的内容为以下图片中

接下来我们可以查看a一下页面效果,当你点击f1页面上的f2的时候会跳转到拦截路由下的f2页面

而同样的当你想实现拦截效果时候,可以点击刷新拦截f2页面,就会出现f2的基本页面

这里我们可以知道你可以通过(...)括号中点的数量来选择层级关系,同样的对于拦截路由会返回到基本的路由页面,你可以实现基础的一些页面功能。

五:并行拦截路由
我们已经学习过路由的高级机制,拦截路由以及并行路由。今天我们演示一下如何让他们更好的团队合作,通过我们之前演示的图片,在一些照片中。当你点击一个照片的时候会被放大。并形成图层的概念。我们可以把底部的照片当作模板。

六:路由处理程序
我们之前已经学过nextjs的文件系统,但实际上应用路由能够实现的功能更多,它允许你使用名为路由处理程序的功能为你的路由创建自定义请求处理程序,这与页面路由不同,它为我们提供了HTML内容路由处理程序,让我们可以构建RESTful端点,完全控制响应,就像构建一个节点加EXpress应用程序一样,你可以使用数据库执行所有的curd操作。路由处理程序正在发出外部API请求也是非常有用的,例如你正在构建一个a第三方能够通讯的应用程序,而同样的nextjs支持多种请求get,post,delete,这里我将向你展示。
npx create-next-app@latest route-handlers-demo
这里我们新建一个nextjs文件,以便我们能够更好的清楚项目路由,同样的我们在app目录下创建一个hello/route.ts文件,这里我们运用了ES6的异步函数,并使用了Response对象返回了一个简单的字符串。
当用户访问/hello路由时,服务器将返回一个包含“Hello, World!”的响应。
同样的路由作为一种文件系统,依然支持创建嵌套的关系,这里我们再此创建dashboard/route.ts ,并在dashboard下创建users/route.ts
这里我们演示一下页面效果,以便你可以清楚的理解他们之间的关系。首先我们先看一下hello请求。

同样的我们再看一下dashboard下的users路由文件

我们已经初步掌握了路由的基本使用,这些已经可以满足你平时开发的日常所需,进一步还需要你能够理解路由是如何嵌套,内部的运行逻辑,这里我们会在接下来的课程讲解关于请求的一些常用知识。同样的你也可以结合自己的想法去写一些自己想要看到的效果。
一处理GET请求
现在让我们更深入了解如何使用路由处理程序,首先是处理get请求,在开始之前我们能应该提到不会专注于本节的ui,这里我们需要引入一个拓展插件,

这里我们会先跳过数据库,转而将数据存在内存中。
这也就意味着,每当我们重启项目的时候,修改之后的数据就会被清除。首先创建一些数据用来处理,这里我们需要创建一个名为comments的文件在app下,并在其下创建data.ts。这里我们用data来存放三条评论。来模仿我们观看视频时,底部的一个效果。

同样的我们需要在路由中引入data数据,当我们发出请求时,就会将其反馈给我们的页面,这里代码结构简单,我们就先不为其准备代码参考了,同样的你可以在data中写入一个列表数据再通过route遍历出来。这也算是我们在视频背后,给你留的一个小的思考,同样的你可以借助AI

这里我们可以在thunder client中来查看APi,这里我们会为你展示两中页面效果


二处理post请求
同样的这两种请求是我们平时最常见的这里我们开始做一个post的请求,添加一条评论在请求中。这里我们开始演示案例。并为你已经准备好了代码,同样的我们post请求依旧是在route.ts当中

export async function POST(request: Request){
const comment = await request.json();
const newComment = {
id: comments.length + 1,
text: comment.text,
createdAt: new Date().toISOString()
};
comments.push(newComment);
return new Response(JSON.stringify(newComment),{
status: 201,
headers: {
"Content-Type": "application/json"
}
});
};
我们先解析这段代码 这里我们采用异步函数处理post请求,并将请求体中的json数据解析出来, 然后将解析出来的json数据添加到comments数组中,并返回新的评论数据。
同样的这里我们用thunder 来测试

可以看到这里已经成功响应,由于我们的数据是保存在内存中,所以基于data.ts的数据并不会发生改变。
动态路由处理程序
这里我们可以通过在请求中,通过访问段地址来处理path和delete请求。这里将会为你以后在做数据库的一些操作时候,提供一些思路。这里同样的我们在comments中创建一个动态的段。
这里我们在comments文件夹中创建 [id]/route.ts ,这里我们看一下目录以便你可以更好的理解,可以看到我们要做的段地址是将comments作为根,这里我们可以用段[id]来去进行一些操作

同样的我们已经为你准备好了代码,目前的代码需要你打好基础,以便你能更好的理解其结构。同样的你可以看相关的nextjs官网,这里有更详细的结构。
import {comments} from "../data"
export async function GET(
request: Request,
{params}:{params:Promise<{id: string}> }
): Promise<Response> {
const { id } = await params;
const comment = comments.find(comment => comment.id === parseInt(id, 10));
return Response.json(comment);
}
这里我们对代码同样的做一个解析// 这里我们是采用了异步函数的方式,通过参数获取id,然后在comments数组中找到对应的评论,并返回json格式的响应。注意,我们使用了async/await来处理异步操作,这样可以确保在获取到评论之后再返回响应。另外,我们还使用了Promise<{id: string}>来定义params的类型,这样可以确保params是一个包含id属性的对象。最后,我们使用了Response.json()来返回json格式的响应。
这里我们演示一下效果可以看到当你在获取id的api的时候,段地址也可以跟随变化。

三处理path请求
这里我们将通过path补丁请求来进行对我们的[id]字段进行更新。同样的我们的代码依然是基于[id]/route.ts ,这里我们先看一下我们的目录。而这里我们增加的请求只有PATCH

export async function PATCH(
request: Request,
{params}:{params:Promise<{id: string}> }
){
const { id } = await params;
const body = await request.json();
const {text}=body;
const index = comments.findIndex(comment => comment.id === parseInt(id, 10));
comments[index].text = text;
return Response.json(comments[index]);
}
这里回到我们的thunder中我们来执行更新评论。

同样的,我们再做一个get请求,来查看我们当前的data,可以看到id:3的状态已经发生改变。这里做出的改变并不会影响data中的数据。

四处理delete请求
这里我们同样的是基于[id]字段,不同的是。这里我们新建的函数为delete.我们目前的删除只的是保存在内存中的data数据
这里我们看一下演示的代码,

export async function DELETE(
request: Request,
{params}:{params:Promise<{id: string}> }
){
const { id } = await params;
const index = comments.findIndex(comment => comment.id === parseInt(id));
const deletedComment = comments[index];
comments.splice(index, 1);
return Response.json(deletedComment);
// 提示删除成功,但是实际上并没有删除,只是从数组中删除了对应的元素。
return Response.json({message: 'Comment deleted successfully'});
}
这里我们是采用了异步函数的方式,通过参数获取id,然后在comments数组中找到对应的评论,并返回json格式的响应。注意,我们使用了async/await来处理异步操作,这样可以确保在获取到评论之后再返回响应。
另外,我们还使用了Promise<{id: string}>来定义params的类型,这样可以确保params是一个包含id属性的对象。
这里我们看一下演示的效果,同样的你可可以根据自己的设计结构来更改我们提供的代码部分。

url查询参数
让我们学习,如何在nextjs中通过url来进行控制查询。现在data中是包含三条数据,现在假设我们想根据搜索词来过滤一些信息。这里我们是在comment/route.ts来做出改动,这里我们先看一下目录。


同样的我们为你准备好了代码
import { type NextRequest } from "next/server";
import {comments} from "./data";
export async function GET(request:NextRequest){
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get("query");
const filteredComments = query
? comments.filter(comment => comment.text.includes(query))
: comments;
return Response.json(filteredComments);
}
这里我们来解析这段代码。这里我们定义了一个GET方法,用于获取评论列表,并根据请求参数中的query参数进行过滤。如果请求中没有query参数,则返回所有评论;否则,返回包含query参数的评论列表。 这里我们采用异步函数处理get请求,并返回过滤后的评论列表。在Next.js中,我们可以在路由文件中定义路由处理函数,这些函数会在请求到达时被调用。接下来我们在thunder中来查看一下我们的参数查询。

这里我们可以改变参数来返回评论。
目前我们已经对查询有了初步的了解,我们目前所见过查询的方式能够满足你平时作为响应请求的需求。接下来让我们继续深度学习路由。这里我们将会接触到一些路由处理程序的基础配置
一路由处理程序的标头
让我们先对标头有个初步的理解。标头表示与API和相关的相应元素,而这些元素可以分为两大类 **请求头**和**用户代理**。它可以向服务器标识浏览器和操作系统,它通常用于特定的浏览器内容渲染和分析我们已经接受了它。
这里我们要使用的文件为api/route.ts ,我们会根据这个页面来做我们的标头参数传递的效果

这里我们为你准备好了代码,你可以根据设置响应请求,来得到你想要的请求数据
import { type NextRequest } from "next/server";
export async function GET (request:NextRequest) {
const requestHeaders = new Headers(request.headers)
console.log(requestHeaders.get("Authorization"));
return new Response('Profile API data');
}
这里 export 出来的函数名就是路由的路径,比如 GET /api/profile 就对应 export async function GET (request:NextRequest)这里的 request 就是客户端发过来的请求,可以从 request.headers 里获取请求头信息 这里返回的 Response 就是响应给客户端的内容
二路由处理程序中的Cookie
首先我们应该先了解一下Cookie,它是服务器发送到用户web浏览器的小块数据。而浏览器可以储存Cookie并将其发送回同一服务器。而请求Cookie有三种途径
- 用途:会话管理 (例如用户购物车,登录)。
- 用途:处理用户个性化和主题偏好
- 用途:跟踪记录和用户行为
如果你想更深入的了解Cookie可以查看相关的文档,以便你可以系统的了解,现在让我们回到vs来看一下如何通过nextj实现使用Cookie的效果。同样的我们为你准备好了代码以便你可以正确的使用Cookie,同样的我们的实现效果依然基于 api/route.ts
import { type NextRequest } from "next/server";
import {headers,cookies} from "next/headers"
export async function GET (request:NextRequest) {
const requestHeaders = new Headers(request.headers)
console.log(requestHeaders.get("Authorization"));
const headersList= await headers();
console.log(headersList.get("Authorization"));
const theme =request.cookies.get("theme");
console.log(theme);
const cookieStore= await cookies()
cookieStore.set("resultsPerPage","20")
console.log(cookieStore.get("resultsPerPage"))
return new Response("<h1>Profile API data</h1>", {
headers: {
"Content-Type": "text/html",
"Set-Cookie": "theme=dark;",
},
});
}
这段代码对你来说会比较难懂,这里我们尽可能把这段代码给你讲的更容易理解,首先我们采用nextjs中内部模块的Cookie,通过请求的字段, 这里我们可以获取到请求头中的Authorization 字段。
底部我们可以设置响应头中的Cookie 字段 ,我们可以设置响应头中的Content-Type 字段 ,并设置响应头中的Set-Cookie 字段。这里我们采用thunder以get的方式来获取api请求。这里我们为你准备了页面的效果。可以看到值发生了改变。

三路由处理程序中的重定向
如何在路由中使用重定向,我们假设你已经构造了一个A页面并且运行了一段时间,你的A页面包含基本的用户信息例如 ID 电子邮件。几个月过后你又构建了更为详细的页面B,而为了将客户端移动到这个B端点,我们可以在A页面设置重定向。从而实现客户端从A到B的过度。这里我们只需要当成知识点来记。 我们需要在头部引入重定向模块。**import {redirect} from "next/navigation";**。同时调用redirect方法。今后我们会在nextjs项目中来对这些进阶知识做一个实例的使用。
四路由处理程序中的缓存
让我们来处理路由的最后细节,nextjs默认的路由是不包含缓存的,但你可以选择在get中使用选择缓存,让我们向你展示它是如何工作的。这里我们创建两个文件包含在app目录下。time/route.ts,以及categories/route.ts。详细的这里我们看一下目录结构以便你不会出现目录不明确的情况。

这里我们先来演示一下不使用缓存来查看我们当前的一个时间。在页面上运行time下的路由页面。这里可以看到它是同步我们当前的客户端的,当我们点击返回时,时间也会不断的刷新。

同样的这里我们引入缓存再进行测试。缓存模块export const dynamic = "force-static",这里我们先来看一下time/route.ts,同样的我们为你准备好了代码。

export const dynamic = "force-static"
export const revalidate = 10
export async function GET() {
return Response.json({ time: new Date().toLocaleTimeString() })
}
这里我们先定义一个路由,然后导出一个函数,这个函数会返回一个json对象,里面包含当前的时间。在顶部我么定义了两个变量dynamic: 这个变量的值为"force-static",表示这个路由是静态的,即不需要服务器端渲染。 revalidate: 这个变量的值为10,表示这个路由每隔10秒重新渲染一次。然后我们定义了一个异步函数GET,这个函数会返回一个json对象,里面包含当前的时间。最后我们导出这个路由,并将dynamic和revalidate变量作为属性导出。
我们可以看一下当前引入缓存过后的样式,当我们再次刷新的时候并不会发生变化。 而是显示的i是我们10秒过后的一个时间

五中间件
这里是路由的最后一个主题,让我们来看一下中间件,它是一个强大的功能可以让你实现拦截和控制整个程序的响应和请求。 这里如果你想要自己更深入的学习中间件可以查看官方的文档,能够使你对这个功能有全面的了解。同样的这里我们作一个案例来简单实现中间件功能。
首先我们需要在src/middleware.ts 。从而实现对中间件效果的一个演示

import {NextResponse} from 'next/server';
import type {NextRequest} from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
const themeProference = request.cookies.get('theme');
if (!themeProference) {
response.cookies.set('theme', "dark");
}
response.headers.set('Cache-Control', 'public, max-age=60, s-maxage=60');
return response;
}
Cookie 是常规标头。在 上,它们存储在标头中。在 a 上,它们位于标题中。Next.js 提供了一种便捷的方式,可以通过 和 上的扩展来访问和作这些 Cookie。这里我们简单做一个代码的讲解, 这里我们定义了一个中间件,用于设置cookie,我们在请求的时候,如果没有theme这个cookie,我们就设置一个默认的theme值。然后我们设置了Cache-Control头部,让浏览器缓存这个请求,这样就可以避免每次请求都设置cookie了。
这里我们来进行一个演示,当你在端口测试的时候会发现cookie的值已经发生改变。

同样的中间件可以实现很多高级的路由功能,这里我们就不做一一的解释,希望你可以通过一些案例来按照自己的喜欢来设置一些路由。
同样的我们也可以通过中间件来实现跨域的处理,这里的话我们可以用代码进行一个简单的测试,我们希望你可以通过这个功能去实现更多自己想要的效果。同样的依旧是我们src/middleware.ts
import { NextRequest, NextResponse } from 'next/server'
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function middleware(request: NextRequest) {
// Check the origin from the request
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Handle preflighted requests
const isPreflight = request.method === 'OPTIONS'
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// Handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
export const config = {
matcher: '/api/:path*',
}
这里我们定义了一个中间件函数,用于处理跨域请求。我们允许来自 https://acme.com 和 https://my-app.org 的请求,并允许跨域请求的预检。对于预检请求,我们返回一个空的 JSON 响应,并设置相应的 CORS 头部。对于简单请求,我们设置相应的 CORS 头部,并返回 Next.js 的默认响应。我们还使用了 Next.js 的路由匹配器,将此中间件应用于 /api 路径下的所有请求。
渲染
这里我们开始在第二部分中解决同样重要的问题,而在enxtjs中渲染听起来很复杂,而实际上也是一个简单而又实用的一个功能,这是一个将组件代码转换为你写入的用户信息,同样的可以实现并看到交互页面的效果,所有这里会是一个重要的功能,我也会在后期开一期关于实战项目的案例同时在我们的git上会为大家准备相应的代码。这里你之前之前听说过 CSR ,SSR,RSC,这里我们会为你讲他们的功能与区别。
一:客户端渲染
这里我们需要看看过去的十年react是如何进行演变的,这里我们来看一下react的渲染策略,服务器通过整个方法发送原始的HtML,其中你的浏览器将react组件转化为你在屏幕上看到的效果。这就是我们能说的客户端渲染,简称CSR,全称为Client-Side of Rendering,然而不久之后,开发人员发现了它存在的问题,开始注意到这种方法有固有的一些缺陷,第一个问题是:当SEO搜索引擎想要去抓取你的网站的时候,由于是用的HTML,从而当查看的时候只会看到是一个空的di盒子,这对于视图来说并不友好,而当你有很多页面进行嵌套的时候,同样的也无法及时加载。第二个问题是:用户体验效果很差,设想一下你的浏览器需要及时反应出页面效果,以及一些逻辑交互。SEO,无法及时的反馈出页面,这对于大部分用户来说是很不友好的,较差的用户体验会让他们觉得体验效果很差,因此我们在接下来的内容中去解决这些问题。

二:服务器渲染
我们刚刚介绍了,属于CSR的两个缺点,现在让我们看看react是如何解决这两个困难,当请求进入的时候,将改变为内容传递。而不再是使用html来使用,从而使用javaScript来构建页面。现在服务器来渲染完整的html,而文档直接进入服务器当中这也就是我们所说的SSR,能够使页面的内容更快的响应,它引入交互方面的复杂性,并在浏览器进行控制,同时使用服务器的内存来进行重建组件tre,并将组件渲染为蓝图,仔细规划出所有,交互元素的应该去向,然后链接javaScript逻辑,理解水合的关键在于接下来的内容,而解决方法也是包含两种
-
一:主要策略静态站点生成
-
二:SSR服务器渲染

三:悬挂SSR
使用SSR中的
,来解决我们上一节的遗留问题,我们使用这个功能来改变游戏的规则 - 服务器上的HTML流
- 客户端选择水化

这里为了防止你不太懂原理,我们将会为你讲解,相比于传统的HTML所实现的全有全无,服务器呈现完整,然后将其发送给客户端,并且只会在javaScript加载之后,才能够满足交互逻辑。但react18为我们解决了这个问题,从用户来看整个页面的效果是加载好然后呈现给用户的,类似于给整个页面进行注水,而通过react18所运用的注水,让我们可以不需要等待所有的页面渲染,而是采用流式传输,同时运用少量的javaScript。能够当你在查看页面的时候已经加载部分的页面

四:服务器组件化
我们已经可以看到react是如何进行渲染从而以更好的方式为用户提供客户端的一个显示,从客户端进行渲染同时演变到服务器渲染,再到后来我们所说的服务器渲染的Suspense,它虽然提高了我们的页面效果,但同时也同样的为我们带来了一些麻烦,例如Suspense虽然接近无缝的渲染,但同样的也消耗大量的性能 ,同时不必要而注水,也让给用户带来了一些延迟。这对于用户来说同样是不友好的,同样的为了应对这一问题,我们更需要一些内部优化的演变,这里就出现了能够及时反应服务器组件RSC,而RSC并不需要进行注水,也不需要进行javaSCript。这也就让用户的体验有了进一步的优化。

五:服务器和客户端组件
这一节我们会通过服务器以及客户端组件来实现我们的案例,好的理论可以帮助你快速理解。这里我们会在案例中具体讲解其中的含义,希望你可以通过这节案例实现更多的效果。同样的这里我们需要新建一个nextjs项目,这里选择你存放nextjs的文件同时执行 npx create-next-app@latest rendering-demo

同样的这里需要我们记好,nextjs+RSC默认输出的是一个服务器组件

这里我们来做一个代码进行演示,我们在app/about,这里可以看到我们为该页面设置了一些盒子以及文字的输出,当我们测试这个端口的时候会提示报错。这里可以通过报错得知我们需要的是将模块更改为客户端的形式。


同样的我们需要再创建一个dashboard/page.tsx ,来进行我们的客户端测试。这里同样的我们可以先看一下目录,以便你可以更好的理解

这里我们在dasobard/page下进行一个客户端的输出,这里我们为你准备好了相关的代码。
"use client";
import React from 'react';
import { useState } from 'react';
export default function DashboardPage() {
console.log('DashboardPage server component');
const [name, setName] = useState("");
return (
<div>
<h1>Dashboard Page</h1>
<label>
Enter your name:
<input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter your name" />
</label>
<p>Your name is: {name}</p>
</div>
);
}
同样的我们对这段代码进行一个简单的解析,我们首先在顶部引用了三个模块。 首先use client标志我们将该页面设置为客户端页面,同样的React,允许我们使用react的一些交互逻辑,useState它允许我们设置状态变量,通过状态变量,我们来控制名字的输出, 这里我们使用 useState 来管理组件的状态。注意:在 Next.js 的服务器组件中,不能使用 useState,因为它们不支持 React 的状态管但是在客户端组件中可以使用 useState,我们在客户端组件中使用 useState 来管理组件的状态,并在渲染时将其传递给服务器组件。

六:RSC渲染生命周期
我们将探究,如何渲染到页面。我们之所以讲解这一方面是让你可以清楚的渲染的由来,当我们谈论服务器组件的时候(RSC),有三者进行参与,你的浏览器,nextjs,React框架

静态渲染
静态渲染是一种服务器渲染策略,在构建应用程序生成HTML页面时,可以将其认为是在任何用户访问之前提前准备好所有的内容,你的页面一旦开始进行建立,就可以被缓存CDN立即提供给用户。而它本身是作为一种预先渲染页面可以在不同的用户之间实现共享,从而显著的提高性能。然后对于静态页面是作为nextjs的一个默认的页面方式,因此你并不需要对其进行构建
动态渲染
当用户发出请求时,为每个用户进行特色渲染。当你需要显示个性化的页面的时候会很有帮助

而对于动态的请求,当路由当中存在动态的数据,段,或者是API,nextjs就会开启动态的渲染来加载这个页面。这里编写一些代码会更容易理解。这里我们用一段代来进行测试,
当我们在更新这个页面的时候,会出现cookie的响应在终端。

七:generateStaticParams
生成静态参数是一个与动态路由一起工作的功能,用于在工作的时候生成动态路由而不是当项目中出现请求,这同时有一个特点就是为我们提供了更好的性能。

同样的在我们的app目录下,创建products/page.tsx

这里我们在products/page.tsx构建一个商品信息列表,这里我们会通过动态路由来体验静态参数

同样的在products/[id]/page.tsx,这里我们会让路由有一个动态的显示

export async function generateStaticParams (){
return [{id:"123"},{id:"456"},{id:"789"}]
}
export default async function ProductPage({
params,
}: {
params: Promise < {id: string} >;
}) {
const {id}= await params;
return(
<h1>
Product {id} details rendered at {new Date().toLocaleTimeString()}
</h1>
);
}
这里我们定义了一个异步函数generateStaticParams,它返回一个数组,数组的每一项都是一个对象,对象中包含一个id属性。 然后我们定义了一个异步函数ProductPage,它接受一个参数params,这个参数是一个Promise对象,这个Promise对象会在渲染页面时被解析,然后我们从params中取出id属性,并渲染出一个页面。注意,我们在ProductPage函数中使用了await关键字,这意味着我们需要等待params Promise对象解析完成,才能取出id属性。 最后,我们在pages目录下创建了一个products/[id]/page.tsx文件,并在其中导出ProductPage函数。 这样,我们就完成了渲染一个产品详情页面的功能。
这里我们在终端运行npm run build会看到我们的页面进行的一个渲染效果,同样的[id]采用的是动态的渲染。在底部可以看到nextjs为我们选择SSG的一种方式

同样的这里我们捕获了这个段地址

八:dynamicParams
之前我们了解了静态的路由参数,它允许我们通过返回一个对象数组来渲染静态的路由段,当然我们可以做一个假设,id不在我们生成的静态参数函数列表会发生什么,而nextjs其实已经我们做好了准备,它仍然会呈现页面,只不过只有在构建项目的时候才会

九:流式处理
这里是我们最后一种服务器渲染策略,流式传输是一种允许从服务器进行渐进式的ui渲染策略。同样的这为用户的体验有了更好的提升,不会等待所有的页面渲染好再进行一个显示。同样的对于这种效果我们可以做一个简单的演化,以便你可以更好的理解页面渲染的效果。
components/product.tsx ,components/review.ts,这里我们定义两个组件, 这里的异步函数会在组件渲染前等待2秒这样做的目的是模拟组件的异步数据获取过程


同样的这里需要我们在app/product-review。product-review/page.tsx,这里我们能看到,我们导入了两个组件:Product和Review。 然后我们在ProductReviewsPage函数组件中,返回了一个div元素,包含一个h1元素和两个Suspense元素。 第一个Suspense元素渲染的是Product组件,第二个Suspense元素渲染的是Review组件。 这两个Suspense元素的fallback属性指定了加载过程中显示的占位符。 这样,当Product组件和Review组件的异步数据加载过程中,页面上会显示一个加载提示。 当数据加载完成后,页面上会显示Product组件和Review组件的内容。

好的让我们看一下页面效果,可以看到我们设置了延迟,会有一个悬念的效果出现

服务器端和客户端组合模式
相信你已经掌握了我们之前所讲的渲染,类似于我们的服务器渲染,动态的渲染,同样的这里我们先对服务器和客户端的组合模式有一个基础的概念,同样的这个组合的运用也是至关重要。

同样的它也为我们提供了多种的实现功能,能够满足我们对日常开发的足够需求。

仅限制于服务器代码
这里让我们来谈论一下客户端服务器分离,考虑到一些代码只会在服务器上使用,例如我们平时需要存放的一些重要信息的认证,考虑处理环境变量的函数和模块,直接与数据库通信,又或者是处理一些敏感信息,因为javaScript可以在服务器与客户端进行共享,而服务器端的代码可能会被客户端检查到,这是一个坏消息。因为它会使我们的信息安全无法保证。而nextjs同样的为我们解决这种问题提供了一种方法,我们可以创建一个名为Server-only的包。
我们来看一下nextjs官方的文档是如何引用服务器组件的,可以看到官方是比较简略的对于服务器的使用,因此这里需要我们自己来做一个案例帮助你更好的理解,同时我们也推荐你多去试着看官方的文档,它可以让你更多理解对于这门语言。

同样的这里我们会做一个案例来进行演示,首先我们需要在app下创建两个文件,server-route,client-route,这里用来做我们的服务器和客户端,同样的我们先看一下目录结构。

client-route

server-route

"use server"
import serverSideFunction from "@/utils/server-utils";
export default async function ServerRoutePage() {
const result = serverSideFunction();
return <div>Server Route Page {result}</div>
}
这里可以看到我们是在顶部进行一个引入组件,同样的在src下创建utils/server-utils.ts,在顶部我们选择是标志为服务器组件

"use server"
export async function serverSideFunction() {
console.log (
`
use multiple libraries,
use environment variables,
interact with databases,
process confidential information,
`
);
return "server result";
};
export default serverSideFunction;
这里让我们启动一下vscode来看一下页面的效果,这里可以看到我们底部已经有了服务器的输出。

你已经看完了nextjs的进阶课程,这些课程知识能够帮助你更好的去实现开发的需求,同样的对于这些知识我们推荐你查看官方的相应文档,它可以帮助你更好的理解这门语言,我们在进阶的nextjs中,所讲的知识是比较琐碎的需要你自己有一个整体的思路,同样的我们接下来会出一个nextjs3的博客来去演示项目案例。




浙公网安备 33010602011771号