Next.js 深入解析
Next.js 深入解析
一、数据获取与渲染策略
1. getStaticPaths - SSG 动态路径生成
用于生成静态页面的动态路由路径。
// 示例:pages/posts/[id].js
export async function getStaticPaths() {
return {
paths: [
// 1. 基础参数形式 pages/posts/[id].js → posts/1
{ params: { id: '1' } },
// 2. 嵌套动态路由 pages/[category]/[slug].js → tech/nextjs-guide
{ params: { category: 'tech', slug: 'nextjs-guide' } },
// 3. 可选catch-all路由 pages/[...slug].js → docs/nextjs/api
{ params: { slug: ['docs', 'nextjs', 'api'] } },
],
// fallback 的三个选项(必选其一)
fallback: false, // 选项1: 只生成指定路径,其他路径返回404
fallback: true, // 选项2: 增量生成,显示加载状态降级方案
fallback: 'blocking' // 选项3: 增量生成,阻塞等待生成完成
};
}
参数说明:
- 输入参数一般为空,
context可能包含locales(支持的语言列表)和defaultLocale(默认语言),但在 Pages Router 中不常用。
2. getStaticProps - SSG 页面数据获取
用于在构建时获取页面所需的数据。
export async function getStaticProps(context) {
// context.params 包含动态路由参数
const { params } = context;
return {
props: {
// 数据会被注入到页面组件
},
revalidate: 60, // ISR 关键:60秒后重新生成
notFound: false, // 如果为true,返回404页面
redirect: {
destination: '/',
permanent: false, // 临时重定向用307,永久重定向用308
}
};
}
重要特性:
props、notFound、redirect三选一返回revalidate是 ISR(增量静态再生)的关键- SEO 友好:永久重定向用
308,临时用307 - 用户体验:
notFound比显示错误页更干净
参数来源:
getStaticPaths返回的动态路由参数- 全局注入的参数(国际化、预览模式等)
3. getServerSideProps - SSR 实时数据获取
每次请求时获取最新数据。
export async function getServerSideProps({ req, res, params, query, locale }) {
// 参数说明:
// req → HTTP 请求对象(包含 cookie、header 等)
// res → HTTP 响应对象(可设置 header、cookie 等)
// params.id → 来自 URL 路由:/user/123
// query → 来自 URL 查询:/user/123?mode=compact
// locale → 来自 Accept-Language header 或 URL
return {
props: {
// 数据会被注入到页面组件
}
// 注意:没有 revalidate 参数
};
}
二、渲染策略对比
| 策略 | 核心代码特征 | 使用场景 |
|---|---|---|
| SSG | getStaticProps() 无 revalidate |
静态内容页 |
| ISR | getStaticProps() + revalidate |
频繁更新内容 |
| SSR | getServerSideProps() |
个性化/实时数据 |
| CSR | useEffect() / useSWR() |
用户交互数据 |
SSG和SSR方案都是在服务端就生成了完整html,所以需要客户端加载后进行水合,
而dynamic 的逻辑是React先渲染loading占位符,当动态组件的JS包下载完成后,React会重新渲染该位置为真实组件,没有水合(直接就是可交互),这一点更像是CSR的方案。
三、动态导入与代码分割
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(
() => import('../components/Hello'),
{
ssr: true, // 默认值,表示服务端加载
loading: () => <p>Loading...</p>
}
);
重要特性:
ssr参数默认为true,表示服务端加载- 主要作用是代码分割:
dynamic包裹的组件会单独打包,按需加载 - 与常规
import不同:常规import的组件会被包含在主包中 - 更像是 CSR 方案:React 先渲染 loading 占位符,JS 包下载完成后重新渲染为真实组件,没有水合过程
dynamic中ssr参数默认为true,当是默认值时,表示服务端加载,此时貌似和不使用dynamic没什么区别,
其主要作用是代码分割,正常import的组件会被默认包含在主包当中,而用dynamic包裹的import组件会单独打包,按需加载。
四、优化功能
1. Image 优化
- 自动图片优化(尺寸、格式、懒加载)
- 支持 WebP 等现代格式
- 防止布局偏移
2. Font 优化
- 自动字体优化和预加载
- 减少布局偏移
3. Script 优化
- 优化第三方脚本加载
- 支持
strategy属性(beforeInteractive、afterInteractive、lazyOnload)
五、路由系统
1. 页面路由(Pages Router)
- 基于文件系统的路由
- 支持动态路由:
[id].js、[...slug].js - 支持嵌套路由
2. 浅路由(Shallow Routing)
const router = useRouter();
router.push(
'/products?page=2',
undefined,
{ shallow: true } // 关键参数
);
使用场景:筛选排序、分页、标签页切换等
- 普通路由跳转:改变 URL → 重新获取数据 → 重新渲染组件
- 浅路由跳转:改变 URL → 直接更新 URL → 保持当前组件状态
浅路由最适合那些只需要更新查询参数的场景,而页面内容可以通过客户端逻辑更新
目前主流使用app router已不常用
3. API 路由
- 在
pages/api/目录下的文件可作为后端接口 - 接收
req和res参数 - 让 Next.js 成为全栈框架
API 路由让 Next.js 从一个前端框架变成了全栈框架,是 Next.js 最重要的特性之一,
简单来说就是一个page下的api文件夹中的文件,都可以作为后端的接口直接访问,参数是req和res。
4. 中间件
- 旧版本:在
pages/目录创建_middleware.js文件 - 新版本推荐:在项目根目录创建
middleware.js文件
六、App Router(现代方案)
更现代化的方案,更推荐使用。核心性能优势是按需水合。有use client声明就水合,没有就不触发水合。
1. 数据获取简化
getStaticProps→ 直接使用async组件函数(默认 SSG,会缓存数据)getStaticPaths→generateStaticParamsexport async function generateStaticParams() { return [{ id: '1' }, { id: '2' }]; } export const dynamicParams = true; // 相当于 fallback: truegetServerSideProps→ 使用以下方式之一:import { unstable_noStore } from 'next/cache'; export const dynamic = 'force-dynamic'; // 强制动态渲染 // 或使用 cache: 'no-store' // 或使用动态函数(如 cookies(), headers())
2. Metadata API
- 用于更好的 SEO
generateMetadata函数可生成动态元数据
3. ISR 实现
export const revalidate = 60; // 每60秒重新验证
// 或
fetch(url, { next: { revalidate: 60 } });
4. 客户端组件
- 需要在文件头部显式添加
'use client'指令 - 默认部分水合:服务器组件不水合,客户端组件水合
5. 动态导入优化
dynamic(..., { ssr: false }) // 主要用来延迟加载
水合条件:
- 组件标记为
'use client'(需要交互) - 组件在服务端渲染了 HTML(
ssr: true或默认)
无水合情况:
- 服务器组件(无
'use client') - 客户端组件但
ssr: false - 纯 CSR 组件(无服务端渲染)
6. 流式渲染
- App Router 自动支持流式渲染
- 需要使用
<Suspense>包裹异步内容 - 每个
<Suspense>边界独立流式传输
7. Server Actions
- 使用
'use server'声明的函数 - 底层是网络通信,但封装为类似本地函数调用的体验
- 简化表单处理,无需创建复杂 API 路由
Server Actions的底层是一次网络通信,但Next.js把它封装成了看似“本地函数调用”的体验。
所以才可以直接使用 'use server'声明之后,其他组件直接调用这个函数就可以了。
既然nextjs中前端代码和后端逻辑在同一个项目中,所以对于简单的表单,省去了创建复杂api的逻辑,直接调函数。
8. 其他特性
- 中间件:在项目根目录的
middleware.js文件中定义,用于身份验证、国际化、重定向等 - 路由组:用
( )包裹文件夹名,用于组织文件夹结构 - Cookie/Headers:在服务端组件中通过
cookies()、headers()函数获取请求信息 - 拦截路由:通过
(.)前缀实现同级目录拦截,常用于模态框 - 客户端导航:
usePathname:获取当前路径(根路径是"/")useSearchParams:获取查询参数useRouter:切换路由- 注意:必须在客户端组件(
'use client')中使用,且usePathname和useSearchParams会导致动态渲染
七、总结
开发建议
- 需要交互的组件:使用
'use client' - 需要延迟加载的组件:使用
dynamic - 需要定期更新的组件:使用
revalidate - 简化数据获取:考虑使用
useSWR进行状态管理
策略选择
- 简单静态页面:使用 App Router 的默认 SSG
- 需要实时数据:使用
dynamic = 'force-dynamic'或unstable_noStore() - 频繁更新内容:使用 ISR(
revalidate) - 用户交互数据:使用 CSR(
useEffect/useSWR)

浙公网安备 33010602011771号