在 Next.js 中,BSR(Browser-Side Rendering)、SSR(Server-Side Rendering)和 SSG(Static Site Generation) 是三种常见的渲染方式,它们各有适用场景:
1. BSR(Browser-Side Rendering,浏览器端渲染)
BSR 指的是在浏览器端动态获取数据并渲染页面。在 Next.js 中,这通常发生在 useEffect() 或客户端 API 请求中。
特点
- HTML 仅包含基本结构,数据由 JavaScript 在客户端请求后填充。
- SEO 不友好,因为搜索引擎可能抓取不到完整内容。
- 适用于用户特定数据(如个性化内容)或数据变化频繁的页面。
示例
import { useEffect, useState } from 'react';
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => setData(data));
}, []);
return <div>{data ? data.message : '加载中...'}</div>;
}
2. SSR(Server-Side Rendering,服务器端渲染)
SSR 指的是 在服务器端渲染 HTML 并返回给客户端。Next.js 使用 getServerSideProps()
来实现 SSR。
特点
- SEO 友好,因为 HTML 是完整的,可以被搜索引擎抓取。
- 页面在每次请求时都在服务器端获取最新数据。
- 性能比 BSR 好,但每次请求都会占用服务器资源,可能会增加负载。
示例
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data } };
}
export default function Page({ data }) {
return <div>{data.message}</div>;
}
3. SSG(Static Site Generation,静态站点生成)
SSG 是 在构建时生成静态 HTML 文件,并在请求时直接返回该文件。Next.js 使用 getStaticProps()
进行 SSG。
特点
- SEO 友好,HTML 是构建时生成的,搜索引擎可以直接抓取。
- 性能最佳,因为 HTML 被预先生成,无需服务器计算。
- 适用于静态内容(如博客、文档等)。
- 适合不频繁变化的数据,但可结合 ISR(Incremental Static Regeneration)动态更新。
示例
export async function getStaticProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data }, revalidate: 10 }; // 10 秒后自动重新生成
}
export default function Page({ data }) {
return <div>{data.message}</div>;
}
总结
渲染方式 | 适用场景 | SEO 友好性 | 性能 | 数据获取时间 |
---|---|---|---|---|
BSR(客户端渲染) | 交互性强、个性化内容 | ❌ 差 | ⬆ 快(但可能影响 FCP) | 客户端请求后 |
SSR(服务器端渲染) | 需要最新数据、SEO 重要 | ✅ 好 | ⬇ 慢(依赖服务器) | 每次请求时 |
SSG(静态生成) | 静态内容、博客等 | ✅ 最佳 | ⬆⬆ 最高 | 构建时(可用 ISR 更新) |
如果页面的数据不会频繁更新,建议使用 SSG(静态生成)提高性能;如果需要每次请求最新数据,使用 SSR(服务器渲染);如果是个性化内容或用户特定数据,使用 BSR(客户端渲染)。
怎么判断我现在的程序使用的是什么呢?
要判断你的 Next.js 页面正在使用 BSR、SSR 还是 SSG,你可以使用以下方法:
1. 通过开发代码检查
在 Next.js 中,不同的渲染模式使用不同的方法:
渲染模式 | 关键方法 |
---|---|
BSR(客户端渲染) | 只使用 useEffect() 或 API 请求,无 getStaticProps() / getServerSideProps() |
SSR(服务器端渲染) | 使用 getServerSideProps() |
SSG(静态站点生成) | 使用 getStaticProps() |
你可以打开 pages/
目录下的文件,检查是否有 getStaticProps()
或 getServerSideProps()
方法:
// 如果有 getStaticProps(),说明是 SSG
export async function getStaticProps() {}
// 如果有 getServerSideProps(),说明是 SSR
export async function getServerSideProps() {}
// 只有 useEffect() 而没有上面两个方法,说明是 BSR(客户端渲染)
2. 通过 Next.js
开发环境的 console.log()
检查
在 getServerSideProps()
或 getStaticProps()
里加 console.log()
,然后在服务器终端(不是浏览器控制台)查看是否打印:
export async function getServerSideProps() {
console.log('SSR 渲染发生了!');
return { props: { time: new Date().toISOString() } };
}
- 如果你在浏览器刷新后,终端(服务器端)有日志,说明使用的是 SSR。
- 如果终端没有日志,而只在构建时打印,说明是 SSG。
- 如果
console.log()
代码在useEffect()
里运行,说明是 BSR。
3. 通过浏览器检查 页面请求方式
方法 1:查看网络请求
- 打开 浏览器开发者工具(F12 / Ctrl + Shift + I)。
- 进入 Network(网络) 选项卡。
- 刷新页面,找到
document
类型的请求(通常是localhost:3000
或你的页面 URL)。 - 查看 响应头(Response Headers)。
判断方式
SSR
- 响应头会包含
x-nextjs-page
,并且cache-control
可能是no-cache
(意味着服务器每次都重新生成)。 - 示例
x-nextjs-page: /my-page cache-control: no-store, no-cache, must-revalidate
- 响应头会包含
SSG
- 响应头
cache-control
可能是public, max-age=...
,表示该页面是静态的,可以缓存。 - 示例
cache-control: public, max-age=3600, stale-while-revalidate=59
- 响应头
BSR
- 页面加载后,会有额外的 API 请求(通常是
fetch('/api/...')
)。 - 只有
document
请求时,返回的 HTML 可能是一个基本结构,数据是客户端动态填充的。
- 页面加载后,会有额外的 API 请求(通常是
方法 2:查看 HTML 源码
- 在页面上 右键 → 查看页面源代码(或者按
Ctrl + U
)。 - 如果数据是完整的,说明是 SSR 或 SSG(服务器已渲染)。
- 如果数据是空的,JS 负责填充,说明是 BSR(客户端渲染)。
示例:
<!-- 服务器渲染 (SSR 或 SSG) -->
<div id="content">Hello, SSR!</div>
<!-- 客户端渲染 (BSR) -->
<div id="content"></div>
<script>
document.getElementById('content').innerHTML = 'Hello, BSR!';
</script>
4. 通过 next build && next start
观察
如果你在 next dev
(开发模式)下不确定,可以运行:
next build && next start
然后:
- 刷新页面
- 观察终端日志
- 结合
console.log()
判断是否有服务器端执行
最终总结
判断方法 | BSR(客户端渲染) | SSR(服务器端渲染) | SSG(静态站点生成) |
---|---|---|---|
代码检查 | useEffect() 进行数据请求 |
getServerSideProps() |
getStaticProps() |
终端日志 | 只在浏览器执行 console.log() |
服务器每次请求都会有 console.log() |
服务器 构建时 执行 console.log() |
网络请求 | 额外的 fetch() 请求 |
document 请求有 cache-control: no-cache |
document 请求有 cache-control: public, max-age=... |
查看 HTML 源码 | 初始内容为空,JS 填充 | HTML 直接包含完整数据 | HTML 直接包含完整数据 |
运行 next build && next start |
不影响 | 每次请求都会重新执行 | 只在构建时执行 |
如果你不确定,建议:
- 查看代码是否有
getServerSideProps()
/getStaticProps()
。 - 用
console.log()
观察是否在服务器端运行。 - 用浏览器开发者工具检查 HTML 和网络请求。
这样你就能判断你的 Next.js 应用使用的是什么渲染模式了!
示例
const handleSaveName = async () => {
try {
setEditing(true)
await updateUserProfile({ url: 'account/name', body: { name: editName } })
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutateUserProfile()
setEditNameModalVisible(false)
setEditing(false)
}
catch (e) {
notify({ type: 'error', message: (e as Error).message })
setEditNameModalVisible(false)
setEditing(false)
}
}
这个用的是什么?
你的 handleSaveName
方法是 客户端渲染(BSR,Browser-Side Rendering),它不涉及 getServerSideProps()
或 getStaticProps()
,而是通过 前端 JavaScript 进行 API 调用并更新 UI。
判断依据
没有
getServerSideProps()
或getStaticProps()
:- 这段代码是
React
组件内部的一个async
方法,而不是 Next.js 提供的getServerSideProps()
或getStaticProps()
。
- 这段代码是
使用了
await updateUserProfile(...)
:updateUserProfile
发送 API 请求(可能是fetch()
或axios
),这是典型的 客户端数据获取(Client-side Fetching)。- 服务器不会在页面渲染时提前获取数据,而是等用户触发
handleSaveName
后才请求。
mutateUserProfile()
:- 这可能是
SWR
或react-query
之类的状态管理库,用于在客户端重新拉取数据,属于 BSR 逻辑。
- 这可能是
setEditing(true)
、setEditNameModalVisible(false)
:- 这些是 React 状态更新(
useState()
),只在浏览器中运行,不涉及服务器端渲染。
- 这些是 React 状态更新(
最终结论
这个 handleSaveName
方法属于 BSR(客户端渲染),因为:
- 数据请求发生在 浏览器端,而不是服务器端。
- 依赖
useState()
和前端notify()
来管理 UI 状态。 - 没有
getServerSideProps()
或getStaticProps()
,不会在页面加载时提前渲染数据。
如何让它使用 SSR?
如果你想让数据请求在 服务器端渲染(SSR) 时执行,而不是在客户端触发,你需要:
把数据获取逻辑放到
getServerSideProps()
里:export async function getServerSideProps(context) { const res = await fetch('https://your-api.com/account/name') const data = await res.json() return { props: { name: data.name }, } }
- 这样
name
在服务器端就会预先加载,页面渲染时已经有数据了。
但你的 handleSaveName
是 用户交互触发的操作,一般不会用 SSR,因为 SSR 适合 初始加载,而 handleSaveName
适合 用户操作后的更新,所以 BSR 是合理的选择!