Nextjs 学习笔记

Posted by CodingWithAlice on January 12, 2025

Nextjs 学习笔记

主要侧重于 13.x 之后给出的 break change ,修正写代码过程中可能遇到的版本问题

升级指南

总结:

  • Link 标签不用再写 a 标签,因为直接解析为 a 标签
  • src/app 目录下所有页面默认是 服务器组件,需要定义 客户端组件 时在顶部添加 “use client”
  • 3、自定义共享布局、样式:src/app/layout.tsx、src/app/global.css
  • 4、自动按照路由进行代码分割
  • 5、预渲染
    • SSG 静态页面生成:getStaticPaths 被改为 generateStaticParams + revalidate - 在构建时生成 HTML + export const revalidate 增量静态再生
      • 增量静态再生 (ISR):运行时 生成或更新 静态页面的能力,能更新动态内容而无需重新构建 - revalidate 配置页面的「重新验证时间」,到点儿后在运行时生成页面,实现静态页面的增量更新
    • SSR 服务端渲染:getServerSideProps - 每次请求时在服务端运行得到「html并嵌入拉取到的动态数据」 -> 返回需要的 HTML + 所需的js代码
  • 6、API Router
  • app 目录中,页面默认是服务器组件;pages 目录中,页面是客户端组件
  • app 目录中,定义客户端组件需要在文件顶部添加 ‘use client’ 指令

1、客户端导航:Nextjs 的前端部分提供内置的「文件系统路由」+ Link

  • Nextjs 13.x 版本开始,<Link> 组件在使用时不用再写<a>标签,因为在渲染时会把 Link 标签解析为 a 标签
    • 实现了「客户端导航」的方式,浏览器不会重新加载整个页面(a标签会触发重新刷新整个页面
  • Nextjs 13.x 版本开始,「文件系统路由」从pages/index.js[旧方式]逐渐改为src/app/page.tsx[新app模式]
    • 旧方式缺点:旧方式容易上手,按文件路径即可推论路由,但复杂嵌套需要写赘余代码
    • 新方式优点:支持 自定义共享布局,在 src/app/layout.tsx 可自定义共享的布局(导航栏等)

2、代码分割:Nextjs 会自动进行

3、页面预加载 prefetching:只要有 Link 标签出现在视口,Nextjs 会自动预取 Link 关联的页面代码 -> 点击跳转时后台已经加载好页面了 -> 秒切

二、静态资源加载

包括 css、image、public 文件

1、内置 css 、 sass、css-in-js

2、支持 css Module:

  • 解决的核心问题: nextjs 中 css 全局命名空间问题,将类名进行局部化处理,使得每个组件的样式只在组件内部生效 + 方便 code splitting 代码分割

  • 使用方式:很简单,把style.css 改写/标记为style.module.css即可,其中的类名会被转换为独一无二的标识符

3、全局变量

  • 旧方法:在 pages/_app.js中 import 引入全局 css 文件,利用 _app.js 是整个应用的顶层组件特性,_app.js包裹了所有页面的组件,引入的样式自然就应用到了全局
  • 新方法:src/app/global.css

三、预渲染

13.x 以上

  • 新的数据获取方式:getStaticProps、getStaticPaths、getServerSideProps 已被 generateStaticParams 替代 - 只会在构建时预渲染来自 generateStaticParams 的参数
    • 增量静态再生 (ISR)
export const revalidate = 60 // Next.js 将在请求进来时使缓存失效,最多每 60 秒一次
// 如果收到一个尚未生成的路径的请求,Next.js 将按需服务器渲染该页面
export const dynamicParams = true // 或 false,对未知路径返回 404
export async function generateStaticParams() {
    const posts = await fetch('https://xxx/blog').then((res) => res.json())
    return posts.map((post) => ({
        id: String(post.id),
    }))
}
export default async function Page({ params }) {
  const post = await fetch(`https://xxx/blog/${params.id}`).then((res) => res.json())
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}
// 这段代码的 增量静态再生 (ISR) 工作原理
-  `next build` 期间生成所有已知的博客文章 (本示例中有 25 )
- 对这些页面的所有请求 (例如 `/blog/1`) 都被缓存并立即响应
- 60 秒过后下一个请求仍然会显示缓存的 (陈旧的) 页面
- 缓存失效并在后台开始生成页面的新版本
- 一旦成功生成Next.js 将显示并缓存更新后的页面
- 如果请求 `/blog/26`Next.js 将按需生成并缓存这个页面
  • 增强 fetch:在 src/app 下的组件可以直接对数据获取,并且可以通过next.revalidate选项来实现增量静态再生(ISR),方便控制数据的更新频率

13.x 以下

默认情况下,Nextjs 会预渲染每个 page(也就是 预先为每个 page 生成 HTML 文件,而不是由 客户端 js 来生成 - 所以下面说的方法都是在「服务端」执行的,都不会被打包进 js bundle 里面

  • 形式1:静态生成 - 生成 HTML 时机是「构建时」,并在每次页面请求时「重复使用」
    • 在生产环境中,运行 next build 为该页面生成一次 HTML,之后被 CDN 缓存(托管 - 快)
    • 在开发环境/本地环境中,getStaticProps runs on every request
  • 形式2:服务端渲染 - 生成时机是「每次页面请求时重新生成 HTML」
1、静态生成(static generation):别名SSG

这种静态生成的页面,分为「不带数据」(默认)的静态页面和「带数据」的静态页面。

需要获取外部数据的静态生成:

  • 页面内容 取决于数据 – getStaticProps
    • 该函数在「构建时」被调用,在预渲染时,将 props 作为参数传入页面
  • [动态路由]paths 路径取决于数据 – getStaticPaths
    • 例如使用 src/app/[id].tsx 来动态访问文件
    • 该函数在「构建时」被调用,指定要被与渲染的数据

nextjs 中 补丁 fetch 在客户端和服务端都可以执行,不需要再 import

// getStaticPaths、getStaticProps - 在同一文件,通过 export 导出 async 的异步函数
function Blog({posts}) {
    // part① A React Component to render this page
    return <ul>{posts.map((it) => (<li>{it.title}</li>))}</ul>
}
export async function getStaticPaths() {
    // part② getStaticPaths which returnsn an array of possible values for ids
    const res = await fetch('http://xxxx/posts'); // 外部查询
    const post = await res.json(); 
    const paths = posts.map(it => ({ params: { id: it.id } })); // 传递 params 数据,params 中的属性名和文档的 [id].tsx 的 key 相同
    return { paths , fallback: false } // 其他非 paths 的路由为 404
}
export async funciton getStaticProps({ params }){ // 入参来源于 getStaticPaths
    // part③ getStaticProps which fetches necessary data for the post with id
    const res = await fetch(`http://xxxx/posts/${params.id}`);
    const post = await res.json(); // 外部查询
    return { props: { posts } } // 向页面传递博文数据
}
export default Blog
2、服务端渲染(server-side rendering):别名SSR/动态渲染

服务端在页面被请求时,执行当前页面内导出的异步函数 getServerSideProps 后再进行预渲染

  • 写法类似于 getStaticProps,只是执行时机不同 - 构建时和请求时

四、客户端渲染

如果不需要预渲染,则可以采用客户端渲染 - 可以组合采用「静态生成(预渲染)不需要数据的部分 + 请求时,在客户端 js 请求拿到数据后再渲染」

五、API Routes - 在 Nextjs 中创建后端 API 的方式

13.x 以上的版本 pages/api/* API 路由已被 route.js (路由处理程序) 特殊文件替代

原理:Nextjs 的路由系统 API Routes,文件名就是路由路径的一部分

  • 客户端向服务端发起这个路径的请求时,Nextjs 就自动调取这个文件来处理请求

语法:文件中导出一个默认函数,接收两个参数

  • req - 请求对象,包含方法、头部信息、查询参数、请求体等
  • res - 响应对象,可以设置请求状态码、头部信息、响应体等

迭代:采用 src/app 的路由方式后,API Routes 的文件也从 pages/api 转移到 src/app/api

  • 常见:src/app/api/user/route.js 定义api/user/ 相关路由
export default function handler(req, res) {
    res.status(200).json({ msg: 'hello, nextjs' })
}
  • 注意事项:不要在 getStaticProps 和 getStaticPaths 中调用 API Route:以为这两个函数都是在预渲染 - 服务端直接执行的,所以可以直接写服务端代码,例如请求数据库等