REACT-NEXT
# 开始
创建一个Next应用
npx create-next-app [NAME] //更新全部依赖到最新版本 pnpm up -r
安装与配置:
-
修改
.eslintrc.json
:{ "extends": ["next/core-web-vitals", "prettier"] }
-
安装
eslint-config-prettier
pnpm i -D prettier eslint-config-prettier # 额外的插件 pnpm i -D prettier-plugin-organize-imports prettier-plugin-tailwindcss
-
配置
.prettierrc.json
:{ "plugins": [ "prettier-plugin-organize-imports", "prettier-plugin-tailwindcss" ], "tailwindFunctions": ["classNames"], "singleQuote": true, "trailingComma": "es5" }
-
配置.vscode
最后,我们在项目中加上
.vscode
文件夹,配置编辑器的扩展和自动校验和修复的设置..vscode/extensions.json
:{ "recommendations": [ // Linting / Formatting "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "bradlc.vscode-tailwindcss" ] }
.vscode/settings.json
:{ // Formatting using Prettier by default for all languages "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, // Formatting using Prettier for JavaScript, overrides VSCode default. "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, // Linting using ESLint. "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact" ], // Enable file nesting. "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "*.ts": "$(capture).test.ts, $(capture).test.tsx", "*.tsx": "$(capture).test.ts, $(capture).test.tsx" } }
# 路由配置
Next支持App路由方式与Page路由方式.
==Page路由的创建方式:==
Next的路由配置同Nuxt的路由配置像似.
-
静态路由
新建pages文件夹于根目录下,并新建index.tsx文件作为localhost:3000的访问界面.
新建post文件夹,index.tsx为localhost:3000/post的访问界面.
在post文件夹下新建first-post.tsx文件夹为localhost:3000/post/first-post的访问界面.
==注意!== Next支持Src/App下启动应用,也支持@/Pages下index路由启动,但是两种方式不可以共存.
若已路由启动应用,则需要删除app文件下的文件.
-
页面导航
页面导航使用Link: import Link from 'next/link' 但是是用
<Link harf:"" />
Link仍旧是使用的客户端导航.
-
动态路由
动态路由便是在相应的文件夹下以[id].tsx之类进行命名的文件.当路由跳转后面携带参数时,将跳转到对应的文件.
动态路由还可以通过[...id].tsx进行命名,这样可以通过.../1/2/3来进行获得id,获得的id将是["1","2","3"].
-
路由器
路由器可以通过
import useRouter from 'next/router'
来引入. -
404界面
创建
pages/404.tsx
实现.
# 浅路由
Next.js中的“浅路由”指的是在客户端仅更新当前页面的URL而不需要重新加载整个页面的功能。它通过next/router
库中的router.push(url, as, options)
方法实现,其中可以传递一个选项对象,包含shallow: true
属性。
浅路由的主要用途包括:
- 保持状态: 当你希望用户导航到一个新的URL但不丢失当前页面的状态或数据时,可以使用浅路由来避免不必要的页面刷新。这在需要保持滚动位置、表单填写内容或其他React组件状态的情况下特别有用。
- 提高性能: 对于同一页面内不同路由参数对应的不同视图,如果这些视图之间的差异可以通过React组件自身的state或props变化来处理,那么使用浅路由就能避免完全重新渲染页面,从而提高应用性能和用户体验。
- 内部导航: 在SPA(单页应用程序)中,许多时候内部页面间的跳转并不需要从服务器重新获取完整的HTML,而是只需要根据新的URL更新页面状态。这种情况下,浅路由提供了在不刷新页面的前提下更改URL的能力。
==浅路由更新时不会再次进行静态生成和服务端渲染,它只是更新当前界面.==
# API路由
API路由主要用于访问后端服务. ==API路由可以用来规范后端返回来的数据.==
- 资源定位:API路由提供了一种结构化的方式来定义和访问应用程序的后端服务中的各个资源或功能。例如,在RESTful API中,不同的HTTP方法(GET、POST、PUT、DELETE等)与特定的URL路径关联起来,使得客户端能够通过发送请求到指定的URL来获取、创建、更新或删除数据。
- 模块化与组织性:通过路由,可以将后端服务划分为多个模块或子系统,每个模块对应一组相关的API接口。这种划分有助于提高代码的可读性和维护性,同时支持团队协作和独立部署。
- 认证与授权:API路由通常结合中间件一起使用,用于实现身份验证和权限控制。可以根据不同的路由配置相应的安全策略,决定哪些用户或客户端有权访问哪些API资源。
- 内容协商与过滤:根据客户端请求的内容类型和参数,API路由可以帮助服务器确定如何响应,包括选择合适的返回格式(JSON、XML等),或者执行条件性的数据筛选和处理。
API路由一般会将 pages/api
目录下的所有文件都作为API端点映射到 /api/*
.
编写方式:
export default function handler(req,res){ //req:请求体 //res:返回体 }
req:
req.cookies
-包含请求发送的cookie的对象。默认为{}
req.query
-包含查询字符串的对象。默认为{}
req.body
-包含由content-type
或null
res:
res.status(code)
-设置状态代码的函数。code
必须是有效的HTTP状态代码res.json(body)
-返回JSON响应.body
必须是一个可序列化对象res.send(body)
-返回HTTP响应。body
可以是string
、object
或Buffer
res.redirect([status,] path)
-重定向到指定的路径或URL.status
必须是有效的HTTP状态代码。如果未指定,status
默认为"307""临时重定向"。
==# 关于API路由在静态生成,服务端渲染与客户端渲染中请求路径的问题==
API路由尽量不要在构建时访问.
- 静态生成,服务端渲染需要使用全局地址,例如localhost:3000/api/getPages.因此静态生成/服务端渲染可以通过加载环境变量来进行请求.
- 客户端渲染请求时可以使用相对地址: /api/getPages
# 资产 元数据 css
**O 资产:**静态资源在public中提供.如果public中有vercel.svg一张静态资源,引用时可以直接引用:
<img src="/vercel.svg" alt="Vercel Logo" className="logo" />
另外,页面加载img推荐使用Next的组件 Image
: import Image from 'next/image'
本地src需要import
使用
layout='fill'
在
fill
布局模式下,图像可以根据其父元素调整大小。考虑使用CSS在页面上为图像的父元素提供空间,然后使用带有fill
、contain
或cover
的objectFit property
以及来定义图像应该如何占用该空间。当使用
layout='fill'
,父元素必须有position: relative
这对于在该布局模式下正确呈现图像元素是必要的。
当使用
layout='responsive'
,父元素必须有display: block
这是
<div>
元素的默认值,但应另行指定。
**O 元数据:**如果想要修改页面的元数据,则可以在相应页面中修改 <Head></Head>
(注意大写).这是Next提供的一个组件.它允许您修改页面的<head>
。
**O css:**Next支持使用 “CSS-in-JS”库,它允许您在React组件中编写CSS,并且CSS样式将被限定范围(其他组件不会受到影响)。
Next.js内置了对styled-jsx的支持.
这段代码 应当写到React组件内部(return里面):
return { ... <style jsx>{` .main { background-color: black; } `}</style> }
O 布局组件: 布局组件一般用Layout进行命名.
export default function Layout({ children }) { return <div>{children}</div> }
在其他页面中,通过引入Layout组件并包裹组件即可.
O 全局样式: 要想在全局加载样式,需要在 pages
文件夹下新建一个_app.tsx
文件并添加内容:
import '@/globals.css'; import { AppProps } from 'next/app'; function MyApp({ Component, pageProps }: AppProps) { return <Component {...pageProps} />; } export default MyApp;
此App
组件是所有不同页面通用的顶级组件。例如,您可以使用此App
组件在页面之间导航时保持状态。
那么既然该组件是所有页面的顶级组件,那么我们就可以创建一个globals.css
全局样式文件,并在该组件中引入达到全局样式的作用
O 脚本优化
- 一些第三方脚本的加载性能很重,可能会拖累用户体验,尤其是当它们被渲染阻塞并延迟任何页面内容的加载时
- 开发人员经常难以决定在应用程序中放置第三方脚本以确保最佳加载
Script组件使开发人员可以更轻松地将第三方脚本放置在其应用程序的任何位置,同时负责优化其加载策略。
import Script from 'next/script' <Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />
strategy
属性来决定何时加载第三方脚本:
beforeInteractive
:在页面交互之前加载 [机器人探测器,Cookie同意管理器]afterInteractive
:(默认):页面交互后立即加载lazyOnload
:在空闲时间加载
# 中间件
创建:middleware.ts
import type { NextFetchEvent, NextRequest } from 'next/server' export function middleware(req:NextFetchEvent,ev:NextRequest){ return new Response('Hello, world!') }
如果您的中间件是在跟路径中创建的,它将在所有路由上运行。相当于全局路由守卫.
如果你想只在某个特定的路由中进行守卫,那么可以在middleware.ts
中进行判断.
# 预渲染与数据获取
Next.js 提供了两种主要的预渲染方式,它们分别是:
-
静态生成(Static Site Generation, SSG)
- 静态生成发生在构建时(build time),Next.js 会为每个静态路由预先生成完整的 HTML 文件。这意味着当用户访问网站时,服务器可以直接发送预生成好的 HTML,从而实现快速加载和出色的 SEO 性能。
- 对于那些内容在构建时就能确定且不依赖实时数据的页面来说,这是理想的选择。这些页面一旦生成后,就可以通过 CDN 进行缓存并分发到全球,显著提高加载速度。
-
服务端渲染(Server-Side Rendering, SSR)
-
服务端渲染则是指在每次客户端请求时动态地在服务器上生成 HTML 页面的过程。这意味着对于每个请求,Next.js 会在服务器端运行 React 应用程序,并将组件渲染成 HTML,然后将其发送给客户端。
-
当页面的内容需要根据用户的特定请求或者访问时的实时数据进行变化时,使用服务端渲染更合适。尽管这相比静态生成增加了服务器端的计算压力,但它能够确保每个用户接收到的是最新的、个性化的内容。
-
在服务端渲染(SSR)过程中,服务器会根据客户端的请求生成对应的组件树,并将其渲染成一个完整的HTML字符串。这个包含所有初始状态和所需数据的HTML页面随后被发送到客户端。
当客户端接收到这个HTML响应时,浏览器会立即解析并显示这个预先填充好内容的页面给用户,从而实现更快的内容呈现和更好的搜索引擎优化(SEO),因为搜索引擎爬虫可以直接抓取到带有实际内容的HTML。
同时,Next.js 会在客户端执行JavaScript代码,初始化React应用。React会比较服务端生成的DOM结构与当前客户端环境中的DOM结构,如果两者不一致或者有额外需要客户端处理的状态或交互逻辑,React将会高效地进行差异更新,仅对必要的部分进行操作,从而实现无缝的客户端接管和后续的交互体验。
-
这两种方式并不是互斥的,在实际项目中可以根据不同页面的需求灵活选择或混合使用。例如,可以为大多数固定内容的页面采用 SSG,而对于包含动态内容或需要用户登录后才能查看的页面,则采用 SSR。
静态生成SSG:
没有数据请求时的静态生成,服务端将直接将预先渲染好的HTML返回给客户端.客户端直接解析展示即可.
有数据请求时的静态生成(需要在构建该页面时就请求数据以填充页面 -- run build ),需要使用到 getStaticProps
:
-
在Next.js中,当你导出一个页面组件时,你也可以导出一个名为
getStaticProps
的async
函数。getStaticProps
在生产环境中构建时运行,并且在函数内部,您可以获取外部数据并将其作为props发送到页面。
本质上,
getStaticProps
允许您告诉Next.js:“嘿,这个页面有一些数据依赖项——所以当你在构建时预渲染这个页面时,一定要先解决它们!”
如下是一个有请求数据的静态生成的例子:
export default function FirstPost({ postsData }: { postsData: any }) { return <article>{postsData}</article>; } export async function getStaticProps() { const res = await fetch('http://jsonplaceholder.typicode.com/posts'); const posts = await res.json(); //注意return出去的数据结构. return { props: { postsData: posts[0].body, revalidate: 60, // 数据将在60秒后重新验证并可能重新生成页面 }, }; }
如果想根据动态路由进行静态生成,则可以通过以下方法:
例如:动态路由是[id].tsx
export default function Article({ paramId }: InferGetStaticPropsType<typeof getStaticProps>) { return ( <div> this is article: {paramId} </div> ); } export function getStaticPaths() { const paths = [ //必须为字符串形式,因为在地址上拼串无论如何都将会是字符串. { params: { id: '1' } }, { params: { id: '2' } }, { params: { id: '3' } }, ]; return { paths, fallback: false, }; } export function getStaticProps(context):getStaticProps { console.log(context.params); return { props: { paramId: context.params.id, revalidate: 60, // 数据将在60秒后重新验证并可能重新生成页面 }, }; }
在上述代码中,getStaticPaths()
用来返回该动态路由的枚举值,只有在 paths
里的动态路由才可以构建,否则就进入404/slug
getStaticPaths
: 用来获取所有可能的ID,可以通过API请求,也可以通过自定义.但是返回必须是paths和fallback.
然后,再在getStaticProps
中获得访问地址的params,从而获得参数,进行请求.
paths
:返回的格式是一个数组,数组中的每个元素都是一个对象,对象的属性为params(这样才能被next识别),这个对象描述了动态路由参数所对应的实际路径.
Fallback
:
若为
false
,则不在path列表中的路由都将返回404.当设置为
true
,Next.js会对预先定义的路径集合生成静态页面,并且对于未提前生成的动态路由,Next.js会在用户第一次访问这些路径时,在后台生成对应的静态页面,同时向用户返回一个占位符或者重定向到另一个页面(如加载状态页或主页)。一旦页面生成完成,后续请求就可以直接命中已经静态化的页面,从而提高性能和用户体验。当设置为
'blocking'
时,Next.js不仅会预先为指定的路径生成静态页面,还会对没有预先生成的、首次访问的动态路由进行服务器端渲染(Server-side Rendering, SSR)。这会导致在服务端立即生成并返回该请求的页面内容,用户可能需要等待稍长的时间,但保证他们能够看到最新的数据。同时,生成后的页面会被缓存起来,后续请求可以直接提供静态版本。
revalidate
: 重新生成静态页面.
如果有用户访问这个页面,且自上次生成以来已经过去了60秒或以上,Next.js会在后台发起请求到服务器重新获取最新的数据,并基于新数据重新生成静态页面。然后将新生成的页面提供给当前和后续的访问者。
因此,
revalidate
不是每过60秒自动更新一次,而是在有用户实际访问并且满足刷新条件时触发更新。这种方式既保留了静态页面加载速度快的优势,又能在一定程度上保持内容的新鲜度。
如果您需要在请求时(请求的接口可能会更新频繁,且需要实时更新在页面上)而不是在构建时获取数据,您可以尝试服务器端渲染:
服务器端渲染SSR
服务器端渲染则会在 每次页面请求时 重新生成页面的 HTML ,构建时不会运行.
要使用服务器端渲染,您需要从页面导出getServerSideProps
而不是getStaticProps
。
==[需要注意的是]== getServerSideProps
与 getStaticProps
不可共存.
例如我们访问的地址是:
http.://localhost:3000/posts/FirstPost?userId=1&title="helloworld"&body="helloworld"
export default function FirstPost({ serverData }: { serverData: any }) { return ( <div> <article>{serverData}</article> </div> ); } export async function getServerSideProps(context: any) { const param = context.query; const res: any = await fetch('http://jsonplaceholder.typicode.com/posts', { method: 'POST', body: JSON.stringify(param), }); const posts = await res.json(); return { props: { serverData: posts.id, }, }; }
其中context对象是一个包含了请求上下文信息的对象.它包含一些属性:
params: 包含动态路由参数(如果页面路径包含[param]这样的动态片段)。
query: 一个对象,包含了查询字符串中的所有参数,例如当URL为/posts?id=123时,可以通过context.query.id获取到123。
等等...
getServerSideProps
和getStaticProps
都可以返回一个重定向路由:return { redirect: { destination: '/', permanent: false, }, };
其中 destination 为重定向地址, permanent:true(301)永久重定向 permanent:false(302/307)临时重定向
当然,如果不需要预渲染页面,还可以使用客户端渲染.
客户端渲染CSR
- 静态生成(预渲染)不需要外部数据的页面部分。
- 页面加载时,使用JavaScript从客户端获取外部数据并填充其余部分。 使用SWR.
SWR
:
# APP路由模式
同nuxt的路由模式.
在src/app目录下,localhost:3000是src/app/page.tsx
创建新的文件夹article,并新建page.tsx,则该文件对应的地址是localhost:3000/article.
此时定义的组件默认是服务器组件.(后续可以设置为客户端组件)
#路由跳转
-
可以使用 import Link from 'next/link' =>
Link跳转时是默认保持滚动位置不变的,如果想跳转重置滚动位置,可以调整option选项:
scroll={false}
-
可以使用 import { useRouter } from 'next/navigation' => useRouter
-
可以使用 window.history.pushState/window.history.replaceState
# 加载状态
即时加载状态是导航时立即显示的兜底用户界面。通过在文件夹中添加loading.js
文件来创建加载状态。
在同一文件夹中,loading.js
将嵌套在layout.js
中。它会自动将page.js
文件和下面的任何子文件包装在<Suspense>
边界中。
因此loading其实是针对当前文件夹下的路径进行loading显示.
*建议:*对路由段(布局和页面)使用loading.js
约定,因为Next.js优化了此功能。
# Streaming
使用SSR,在用户查看页面并与之交互之前,有一系列步骤需要待完成:
- 首先,在服务器上获取给定页面的所有数据。
- 然后服务器为页面呈现超文本标记语言。
- 页面的超文本标记语言、CSS和JavaScript被发送到客户端。
- 使用生成的超文本标记语言和CSS显示非交互式用户界面。
- 最后,反应水合物用户界面使其具有交互性。
当您想防止长数据请求阻止页面呈现时,流式传输特别有用,因为它可以减少第一个字节的时间(TTFB)和首次内容绘制指标.它也有助于改善页面可交互时间(TTI),尤其是在速度较慢的设备上。
# 布局
布局分为根布局和分布局,但是他们的名字都是layout.tsx.
只有根布局可以包含
<html>
和<body>
标记。
页面进行加载时将自动加载布局.
# 模板
模板是在当前路径的layout.tsx下,下一个路径的layout之上的存在.且命名必须时template.tsx
export default function Template({ children }: { children: React.ReactNode }) { return <div>{children}</div> }
在Next.js中,
template.tsx
和layout.tsx
通常都是用来组织页面布局的组件,但它们可能根据项目的具体实践有所不同。
- Layout.tsx:
- 这个文件名在Next.js项目中被广泛使用来定义一个通用的布局组件,它通常包含页面中的共享元素,如导航栏、页脚、侧边栏等。
- 在每个页面中(例如通过
pages/_app.tsx
或者单独的页面组件)导入并使用这个布局组件,可以保证整个应用具有统一的外观和结构。- 通过在每个页面内部包裹特定内容,布局组件可以帮助开发者轻松地在整个应用范围内管理样式和公共部分。
- Template.tsx:
- 虽然名称上有所区别,但在一些项目中,
template.tsx
可能会扮演类似的角色,同样用于定义一个可复用的页面模板或框架。- 不过,在某些场景下,
template.tsx
可能指的是特定类型页面的模板,比如博客文章模板、产品详情模板等,这取决于开发者的命名习惯和项目架构设计。总结来说,
layout.tsx
更倾向于全局通用的布局,而template.tsx
可能是针对某种特定类型的页面布局。两者都旨在提高代码复用性和维护性,实际使用时请参考具体的项目结构和约定。
# 修改元数据
对于每一个页面或者布局,都可以通过:
import { Metadata } from 'next' export const metadata: Metadata = { title: 'Next.js', }
来修改相应的head及其他的元数据.