前言
在开始项目之前,我们需要了解什么是CSR和SSR? 为什么我们要学习Next.js?
为什么要学习Next.js
-
React生态系统:Next.js是建立在React之上的,它提供了许多增强的功能和工具,使得开发React应用程序更加容易和高效。
-
服务器渲染(SSR):Next.js支持服务器渲染,这意味着你可以在服务器上预渲染页面,然后将其发送到客户端。这可以提高页面的加载速度和搜索引擎优化(SEO)。
-
静态网站生成(SSG):Next.js还支持静态网站生成,这意味着你可以在构建时预渲染整个网站,并将其作为静态文件部署。这可以提高网站的性能和安全性。
-
路由和导航:Next.js提供了强大的路由和导航功能,使你能够轻松地管理应用程序的不同页面和导航。
-
数据获取:Next.js提供了多种数据获取方法,包括从API获取数据和从数据库获取数据。这使得与后端集成变得更加容易。
-
插件和扩展性:Next.js具有丰富的插件生态系统,可以轻松地扩展和定制的应用程序。
CSR(客户端渲染)
是一种网页开发技术,其中网页内容的渲染主要由用户的网络浏览器(客户端)而不是服务器执行。在客户端渲染中,Web服务器通常向客户端发送原始数据或最小的HTML框架,然后在客户端的浏览器中运行的JavaScript获取额外的数据并动态呈现最终的网页。因此,用户在所有过程完成之前无法看到任何内容。
优点
- 减少了服务器请求页面的次数
- 快速交互(因为它一次性加载所有页面)
- 页面切换时没有闪烁
缺点
- SEO差(搜索引擎优化)
- 初始加载速度慢(因为它一次性加载所有页面)
- 首次访问后加载速度快(因为所有页面都已缓存)
SSR(服务器端渲染)
是一种网页开发技术,其中Web服务器在用户请求页面时生成网页的HTML内容,并将完全渲染的HTML发送到客户端的网络浏览器。因此,用户可以在进程进行中看到服务器上预渲染的HTML界面。
优点
- 良好的SEO(因为页面在服务器上完全呈现,搜索引擎可以轻松爬取和索引内容)
- 初始加载速度快(因为它只加载特定页面)
缺点
- 频繁的服务器请求页面创建
- 交互速度较慢
- 页面切换时有闪烁效果
创建 Next.js项目
- 安装
npx create-next-app@latest my-project
- 偏好选项配置
√ TypeScript(Yes)
√ ESLint (Yes)
√ Tailwind CSS(Yes)
√ src/
?(No)
√ App Router(推荐)(Yes)
√ 您想自定义默认导入别名吗?(Yes)
- 运行
npm run dev
- 浏览
Next.js 14版本中的新功能
app目录
在之前的Next.js版本中,所有页面都应该生成在pages目录中(页面路由器)。对于新应用程序,Next.js团队建议使用App Router(应用路由器)。这个路由器允许开发者使用React的最新功能,并且是基于社区反馈的先前版本的演变。
Streaming and Suspense (流式)
另一个功能是使用页面中的Suspense
边界将内容流式传输到浏览器。例如,页面的某个部分正在从服务器获取数据。不要为整个页面显示加载消息,而是提供其余页面,并且只在包含获取数据的特定部分显示加载消息,同时,其余页面不受包含获取数据的部分的影响。
客户端组件和服务器组件
有两种创建组件的方式。
- 客户端组件
- 添加
use client
,声明是客户端组件 - 在服务器上渲染并在浏览器中进行Hydrated(水合作用)
- 使用场景,如果组件包含交互操作(例如useState,useEffect,onClick,onChange ...)
- 服务器组件
- 默认是服务器组件(无需声明)
- 在服务器上呈现,无需Hydrated(水合)
- 使用场景,组件不包含任何交互操作
建议尽可能多的使用服务器组件,如果应该使用客户端组件,建议单独创建客户端组件,然后导入他。
什么是Hydration(水合作用)
将交互功能添加到预渲染的HTML的过程。
路由
在之前的Next.js版本中,文件名用于路由。
/news => news.tsx
在13版本中,创建包含页面的目录和文件
/news => news/page.tsx
如果有嵌套路由,
/news/latest => news/latest/page.tsx
app
├── news
│ ├── latest
│ │ └── page.tsx (/news/latest)
│ └── page.tsx (/news)
布局
而在App Router中,直接保存在app目录下的layoutx.tsx
文件会应用于所有page.tsx
文件。layout.tsx
是根布局文件,是必须的,根布局必须定义<html>
和<body>
标签,因为 Next.js 不会自动创建它们。
根布局默认是服务器组件,不能设置为客户端组件。
链接(Link)
要使用Link
组件,需要导入next/link
。
import Link from "next/link";
导航(Navigation)
Next.js提供了有用的导航功能(useRouter,notFound…)
使用useRouter()
我们可以写编程式导航,但是它只能在客户端组件内使用。
import { useRouter } from 'next/navigation'
图片(Image)
使用Next.js的<Image>
而不是<img>
,要使用必须要引入。
import Image from 'next/image'
loading和error和not-found
在ReactJS中,为了反映页面的加载或错误,我们需要创建每个状态并更改状态以进行重新渲染。此外,如果用户访问未指定的路由,我们需要在路由处理中使用'*'设置404页面路由。
在Next.js中,我们可以在不设置状态或路由的情况下显示每个页面。我们只需要创建一个名为(loading和error和not-found)的文件。(当然,我们可以像在ReactJS中那样设置状态,但是应该为客户端组件声明'use client')
-
loading.tsx(或loading.jsx) 当api调用正在处理或页面正在加载时,将自动呈现此文件。
-
error.tsx(或error.jsx) 当发生错误时,将自动呈现此文件。
-
not-found.tsx(或not-found.jsx) 当返回notFound()或访问未指定的路由时,将呈现此文件。
import { notFound } from 'next/navigation';
// ...
if (!condition) return notFound();
动态路由
- 动态参数
可以通过将文件夹的名称用方括号括起来来创建动态段:
[folderName]
。例如,[id]
或[slug]
。
app
├── news
│ ├── [id]
│ │ └── page.tsx (/news/1 或者 /news/2)
它是通过props传递的。因此,我们可以通过props.params.id
获取参数数据('id'是你在路由目录名称中指定的名称)。
- 设置静态参数
所以每当我们点击一条新闻,就可以通过动态路由来处理,然而,每次都要获取所选新闻数据并进行服务器端渲染。那么这些都是需要时间来处理的。在这一点上,我们可以使用generateStaticParams
,用静态参数替换处理动态参数。
我们需要在文件中创建generateStaticParam
s函数。并调用API来接收所有新闻ID,并将它们作为props发送到组件,以下是示例代码。
const News = async ({ params }: { params: { id: string }}) => {
return (
<section>
<div>News Detail</div>
<Suspense fallback={<Loading />}>
<NewsDetail id={params.id} />
</Suspense>
</section>
);
};
export default News;
export async function generateStaticParams() {
const news: INews[] = await getAllNewsAPI(); //获取所有新闻数据
return news.map((news: INews) => {
return { id: news.id.toString() }; // 匹配
});
}
我们可以在构建的时候检查,是否是静态的。
- 对于动态参数,此页面将是
λ /news/[id]
这意味着每当请求到来时都会生成页面(也实现API调用)
- 对于静态参数,此页面将是
● /news/[id]
/news/1,/news/2…
这意味着当请求到来时不会生成页面(也不会实现API调用)。
API
Next.js提供了构建公共API的解决方案。在解释公共API之前,我想分享一些处理外部获取数据的概念。
- force-cache
如果我们发送用于获取数据的请求,响应的数据将被缓存。这些缓存的数据将保留,直到手动失效。类似于Next.js 12中的getStaticProps
(SSG - 静态站点生成)。force-cache
是默认值,可以省略。它可用于不经常更改的数据。
fetch('URL', { cache: 'force-cache' });
- no-store
如果我们发送用于获取数据的请求,响应的数据应该在每个请求上重新获取。类似于Next.js 12中的getServerSideProps
(SSR - 服务器端渲染)。它可用于频繁更改的数据。
fetch('URL', { cache: 'no-store' });
- Revalidate
如果我们使用force-cache
进行请求,数据将被缓存。如果响应数据发生更改怎么办?然后,我们应该重新构建应用程序并再次部署。为了避免这项工作,作为替代,我们可以使用Revalidate
选项。该请求应该被缓存一段时间,并在一段时间后重新验证。类似于Next.js 12中具有revalidate
选项的getStaticProps
(ISR - 增量静态再生)。
fetch('URL', { next: { revalidate: 30 } });
// 数据将被缓存30秒。
// 30秒后,将在后台重新获取。
// 后台意味着,如果您在30秒后刷新页面,数据不会更改,但后台的真实数据会被重新获取。
// 如果再次刷新,将呈现新的重新获取的数据。
API目录和路由文件
在应用程序目录中,可以在api目录中处理所有的API。在api目录中,您可以设置路由名称目录。最后,为配置创建route.ts
或route.js
文件(设置GET / POST / PUT / DELETE方法)。
app
├── api
│ ├── news
│ │ └── route.ts (/api/news)
- GET
import { NextResponse } from "next/server";
// http://localhost:3000/api/news (GET)
export async function GET() {
return NextResponse.json({ ... });
};
// http://localhost:3000/api/news?title=sports&published=today (GET)
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = searchParams.get('title');
const published = searchParams.get('published');
return NextResponse.json({ ... });
};
- POST
import { NextResponse } from "next/server";
// http://localhost:3000/api/news (POST)
export async function POST(request: Request) {
const { title, published }: Partial<INews> = await request.json();
if(!title || !published) return NextResponse.json({ message: '没有数据' });
return NextResponse.json({ ... });
};
- PUT
import { NextResponse } from "next/server";
// http://localhost:3000/api/news (PUT)
export async function PUT(request: Request) {
const { title, published }: Partial<INews> = await request.json();
if(!title || !published) return NextResponse.json({ message: '没有数据' });
return NextResponse.json({ ... });
};
- DELETE
import { NextResponse } from "next/server";
// http://localhost:3000/api/news (DELETE)
export async function DELETE(request: Request) {
const { id }: Partial<INews> = await request.json();
if(!id) return NextResponse.json({ message: '没有数据' });
return NextResponse.json({ ... });
};
评论(0)