一、创建项目
npx create-next-app@latest 二、基于文件的路由机制

三、动态路由参数
注意:在匹配 URL 时,静态路由优先权大于动态路由
1、基础使用

http://localhost:3000/about/1
http://localhost:3000/about/1
import {useRouter} from 'next/router' //引入钩子函数export default function AboutProjectPage() { const router = useRouter() //使用钩子函数 return ( <> <div> About Project. The id is: {router.query.id} //获取动态路由参数 </div> </> )} 2、多层路由参数
还可以使用多层的路由参数


3、多个参数


![]()
变成数组

四、路由跳转
1、Link
(1)、基本使用
import Link from 'next/link'; //引入<Link href="/"> //使用 首页</Link> (2)、传入UrlObject
interface UrlObject { auth?: string | null | undefined; hash?: string | null | undefined; host?: string | null | undefined; hostname?: string | null | undefined; href?: string | null | undefined; pathname?: string | null | undefined; protocol?: string | null | undefined; search?: string | null | undefined; slashes?: boolean | null | undefined; port?: string | number | null | undefined; query?: string | null | ParsedUrlQueryInput | undefined;} 
2、编程式导航(router.push、router.replace)


3、404页面
pages 文件夹下创建特殊文件 404.js,Next.js 将会在返回 404 错误时,自动加载组件。
相当于用户可以自定义 404 页面。
五、静态文件
根目录 public 目录下放静态文件,Next.js 会自动处理,放在这个文件夹外的静态文件是无法获取到的。
六、css模块
1、组件样式
Next.js 通过 js组件的名称.module.css 文件命名约定来支持 CSS 模块。
只影响所绑定的那个组件

2、全局样式
在根目录下的styles写globals.css

引入
![]()
七、SSG
SSG 是静态站点生成,就是在文件打包阶段,预先生成页面。
Next.js 默认会预渲染所有没有动态数据的页面,而动态的数据还是像 React 一样在客户端渲染的。
如果要在 HTML 源码中展现动态数据,可以使用 page 下 getStaticProps 方法。这个方法是跑在服务端环境下的,可以在服务端获取数据并渲染,并且客户端不会收到方法内任何的代码。
此外,Next.js 拓展了一些功能,比如 fetch 是浏览器的接口,在服务端是不能用的,而在getStaticProps 方法中是可以使用 fetch API 的。
1、getStaticProps
getStaticProps 方法返回值类型如下,一共有三种情况:
export type GetStaticPropsResult<P> = | { props: P; revalidate?: number | boolean } | { redirect: Redirect; revalidate?: number | boolean } | { notFound: true; revalidate?: number | boolean } (1)、基本使用

一般正确获得数据后,返回值是第一种情况。
(2)、revalidate

(3)、redirect、notFound

获取数据失败时,引导用户进行下一步操作,重定向或直接返回404错误。
(4)、获取动态路由参数(context)

pages/[pid].js

无法跳转到[pid].js
因为[pid]是动态的,nextjs无法提前渲染
要借助 getStaticPaths
2、getStaticPaths
文件名带[]的js如果需要生成静态页面,需要使用 getStaticPaths 方法。
getStaticPaths 方法定义了一组需要生成静态页面的列表,每项数据都会调用 getStaticProps 来获取数据,所以要使用 getStaticPaths 一定先要有定义 getStaticProps。
(1)、基本使用
pages/[pid].js

此时打开页面,是将所有的json全部发送给浏览器

(2)、fallback
fallback 值为 false 时,如果 URL 请求参数不在 paths 属性中定义了,那么会直接返回404页面。fallback 值为 true 时,如果通过 Link 组件在页面中导航,那不会有问题。但是如果是直接在 URL 中访问未在 paths 中列出的路径,会直接报错,需要在 React 组件中判断对应 props 的参数,在服务器还未准备好时,先返回一个加载中的提示。
除了布尔值,fallback 还可以赋值为 'blocking',请求页面时如果数据未准备好就阻塞请求,等待页面渲染完毕后再返回页面。相比第二种情况,相当于免除了组件中判断这一环节。
fallback 值为 true 时,如果访问不存在的请求路径,就可以在 getStaticProps 中直接返回 { notFound: true } 来返回 404 页面。
八、SSR
SSR 是服务端渲染,getServerSideProps 方法可以针对每次请求作出处理,适用于数据变化比较频繁的页面。
getStaticProps 与 getServerSideProps 只能二选一。
getServerSideProps 也是运行在服务器上的方法,这个方法的参数 context 可以完整获取请求的所有数据,context 的类型如下:
export type GetServerSidePropsContext< Q extends ParsedUrlQuery = ParsedUrlQuery, D extends PreviewData = PreviewData> = { req: IncomingMessage & { cookies: NextApiRequestCookies } res: ServerResponse params?: Q query: ParsedUrlQuery preview?: boolean previewData?: D resolvedUrl: string locale?: string locales?: string[] defaultLocale?: string} getServerSideProps 返回值类型:
export type GetServerSidePropsResult<P> = | { props: P | Promise<P> } | { redirect: Redirect } | { notFound: true } getServerSideProps 的返回值类型基本同 getStaticProps,只是少了 revalidate 属性,因为getServerSideProps 会对每次请求进行重新渲染。
(1)、基本使用

九、不适合预渲染的情况
数据变化非常频繁的页面(比如股票数据)与用户身份高度耦合的页面(比如用户最近xxx的xxx)页面中只有某一小部分数据不同的情况碰到这些情况,还是在客户端使用 useEffect 中 fetch 来获取数据,Next.js 团队也编写了一个React 钩子库 SWR(https://swr.vercel.app/zh-CN) 来简化客户端请求,示例如下:
每次当页面获得焦点时,swr钩子都会重新发送请求获取数据
npm i swr import useSWR from 'swr'const fetcher = (...args) => fetch(...args).then((res) => res.json())function Profile() { const { data, error } = useSWR('/api/profile-data', fetcher) if (error) return <div>Failed to load</div> if (!data) return <div>Loading...</div> return ( <div> <h1>{data.name}</h1> <p>{data.bio}</p> </div> )} 十、增加Meta信息
1、基本使用

2、Head组件复用


3、全局通用Head
全局通用的 Head 组件可以添加在 _app.js 这个文件中。此外,相同的头标签会合并,合并的规则就是最后渲染的 Head 覆盖之前渲染的 Head。

另一个全局特殊 JS 文件是 /pages/_document.js,这个文件的默认值如下:
import { Html, Head, Main, NextScript } from 'next/document'export default function Document() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html> )} _app.js 这个文件相当于 body 中的内容,_document.js 相当于整个 HTML 文档,比前者更“外面”。注意这里的 Head 组件的引入包与普通页面引入的包不同,不要搞错。
十一、图片优化
Next.js 提供了优化图片的方案——Image 组件。
使用 Image 组件有四点好处
对各个设备使用合适的尺寸与格式(使用Chrome访问页面时,图片会转换成webp格式)防止Cumulative Layout Shift(累计布局偏移)图片在视图中才会被加载可以自定义图片尺寸// ...import Image from 'next/image'export default function About(props) { return <> {/* ... */} <Image src={'/img.jpeg'} alt="图片" width={100} height={100} /> <img src={'/img.jpeg'} alt="图片" /> </>}
Next.js 会根据 Image 的 width 与 height 值,在页面请求服务端时,转换并缓存相应大小的图片。
十二、API 路由
/pages/api 文件下的 JS 文件不会导出页面组件,Next.js 会将这些文件映射成 /api/* 的 API 端点。Next.js 团队在 NodeJS 的 http 模块之上封装,提供了类似 express 的 web 服务器开发功能。
我们可以在这这些文件里写服务端的逻辑,同 getStaticProps 方法一样,这些逻辑客户端是看不到的。
这些API路由的基本格式如下:
export default function handler(req, res) { if (req.method === 'POST') { // 处理POST请求 } else { // 处理其他HTTP方法请求 }} 1、基本使用
pages/index.js

post请求

get请求

pages/api/feedback.js
post请求


get请求

2、动态api路由


使用

十三、部署Next.js
1、构建
构建 Next.js 应用有两种方式:
(1)、next build
第一种是“标准构建”,使用命令 next build 构建,课程之前构建都是使用这种方式。
使用这种方式构建,我们会得到优化后的前端项目 + 一个 NodeJS 服务端程序。这个服务端程序提供了 API 路由、SSR 与页面重验证等功能。所以如果要部署这个应用,需要服务器有NodeJS 环境。
(2)、next export
第二种构建方式是静态打包,使用命令 next export 构建。
使用这种方式生成的代码,只会包含纯前端的内容,HTML、CSS、JS 以及静态资源。没有 NodeJS 服务端程序,所以部署可以不需要 NodeJS 环境。当然这样的话,API路由、SSR 等 Next.js 提供的特性就不能使用了。
2、配置
项目根目录 next.config.js 文件,可以对 Next.js 进行配置。
/** @type {import('next').NextConfig} */const nextConfig = { reactStrictMode: true,}module.exports = nextConfig
这个文件中的代码也是服务端代码,在构建过程中以及构建生成的 NodeJS 服务端程序中会使用到。此外这个文件不会被 Webpack, Babel 或 TypeScript 处理,所以确保使用与机器NodeJS 版本相匹配的语法。
具体配置项看官方文档
十四、加密
课程使用 bcryptjs 包来实现加密逻辑,通过下列命令安装:
npm i bcryptjs bcryptjs 包我们只要关注两个函数,分别是加密 hash 和比较 compare 。注意两个方法都是异步的。
import { hash, compare } from 'bcryptjs'// ...// 通过 hash 函数加密明文密码const hashedPwd = await hash(pwd, 12)// ...// 通过 compare 函数比较两个密码是否相同,返回布尔值const isValid = await compare(newPwd, hashedPwd) 十五、鉴权
npm install next-auth 1、后端生成jwt令牌
next-auth 包提供了前后端鉴权需要的逻辑,在 API 路由中,创建特殊文件 /api/auth/[...nextauth].js,在文件内引入 next-auth 包并实现相关逻辑:
import NextAuth from "next-auth"import CredentialsProvider from "next-auth/providers/credentials"export const authOptions = { providers: [ CredentialsProvider({ name: 'Credentials', session: { strategy: "jwt", }, async authorize(credentials, req) { // 自己实现验证逻辑 const res = await fetch("/your/auth", { method: 'POST', body: JSON.stringify(credentials), headers: { "Content-Type": "application/json" } }) const user = await res.json() // 一切正常返回用户信息 if (res.ok && user) { return user } // 用户信息没获取到就返回null return null } }) // ...其他 providers ],}export default NextAuth(authOptions) 2、在前端组件中,使用next-auth/react模块的signIn函数登录
import { signIn } from "next-auth/react"export default () => <button onClick={() => signIn('credentials', { redirect: false, username: 'username', password: 'password' })}>登录</button> 3、客户端通过 useSession 钩子获取鉴权信息
import { useSession } from "next-auth/react"export default function About(props) { const { data: session, status } = useSession() console.log('session ', session) console.log('status ', status) return <> {/* ... */} </>} v4版本 next-auth 需要 SessionProvider 才能使用上面这个钩子,在 _app.js 中增加相关代码:
import {SessionProvider} from "next-auth/react"import '../styles/globals.css'export default function App({Component, pageProps: {session, ...pageProps},}) { return <SessionProvider session={session}> <Component {...pageProps} /> </SessionProvider>} 4、使用 next-auth/react 模块的 signOut 函数登出用户:
import { signOut } from "next-auth/react"export default () => <button onClick={() => signOut()}>登出</button> 十六、路由守卫
在客户端侧,可以使用 getSession 获取当前的鉴权信息,在需要权限的页面,可以判断 session 的值进行进一步操作。
在服务端侧,可以在 getStaticProps 方法中使用 unstable_getServerSession 函数来获取 session,v4版本中,getSession 不可以在服务端使用。
import { unstable_getServerSession } from "next-auth/next"import { authOptions } from "./api/auth/[...nextauth]"export async function getServerSideProps(context) { return { props: { session: await unstable_getServerSession( context.req, context.res, authOptions ), }, }}