1. 当浏览器遇到React的不同姿势
记得我刚接触React那会儿,所有教程都教我npm run start
启动开发服务器。但当我真正要把项目部署上线时才发现:原来在客户端(CSR)、服务端(SSR)和静态生成(SSG)这三种渲染模式中,那个可爱的开发服务器模式是最不能直接用在生产环境的。
(示例开始:技术栈Next.js v13+)
// 最基础的CSR组件
function HomePage() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
}, [])
return <div>{data ? data.message : 'Loading...'}</div>
}
这经典的useEffect+fetch
模式在复杂应用中会导致:
• 白屏时间延长(需要等待JS下载执行完毕才会发起请求)
• SEO效果差(首次渲染空内容)
• 首屏性能问题(特别是低端设备)
2. SSR的魔法时刻
当我在电商项目中接手商品详情页优化时,发现用户从搜索结果页点击进入需要完整加载2秒以上。这时团队决定引入SSR方案:
(示例:Next.js的SSR实现)
export async function getServerSideProps(context) {
// 服务端获取数据
const { params } = context
const productId = params.id
const res = await fetch(`https://api.example.com/products/${productId}`)
const product = await res.json()
return {
props: {
product,
},
}
}
function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>价格:{product.price}</p>
</div>
)
}
运行效果变化:
- 请求到达Node服务器
- 服务器执行
getServerSideProps
- 生成完整HTML返回
- 浏览器立即显示完整内容
实施后首屏加载时间缩短40%,Google爬虫收录率提升3倍。但每次请求都要执行完整的服务端渲染,当流量突增时服务器CPU使用率就会告急。
3. SSG的静默革命
后来负责公司技术博客迁移时,我完全切换到SSG模式:
(示例:Next.js的静态生成)
// 生成所有可能路径
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts')
const posts = await res.json()
const paths = posts.map(post => ({
params: { id: post.id },
}))
return { paths, fallback: false }
}
// 构建时生成页面
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`)
const post = await res.json()
return {
props: {
post,
},
}
}
function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
)
}
该方案的关键优势: • 构建完成后CDN直接托管HTML文件 • 突发流量下也能保持毫秒级响应 • 完全避免服务端渲染的计算开销
不过当我们需要更新博客内容时,就需要重新触发构建。这时候可以结合Webhook实现自动构建,但内容更新存在分钟级的延迟。
4. 关键决策因素分析表
评估维度 | SSR优势场景 | SSG优势场景 |
---|---|---|
数据更新频率 | 每分钟多次变化(股价) | 每周变更(产品文档) |
内容个性化 | 需要鉴权数据(订单页面) | 公共通用内容(法律条款) |
SEO优先级 | 需要预渲染的SEO关键页面 | 纯静态内容的高性能呈现 |
基础设施成本 | 需要维护服务器集群 | 完全静态托管0服务器成本 |
开发复杂度 | 需处理服务器端状态 | 纯静态构建简单可靠 |
5. 实战中的混合策略
最近在开发营销活动页面时,我采用了一个混合方案:
export async function getStaticProps() {
// 静态生成公共部分
const res = await fetch('https://api.example.com/campaign')
const baseData = await res.json()
return {
props: {
baseData,
// 动态部分将在客户端加载
},
revalidate: 60 // 增量静态再生
}
}
function CampaignPage({ baseData }) {
// 客户端动态加载个性化内容
const [userData, setUserData] = useState(null)
useEffect(() => {
fetch('/api/user-data')
.then(res => res.json())
.then(setUserData)
}, [])
return (
<div>
<header>{baseData.title}</header>
{userData && <div>专属福利:{userData.discount}</div>}
</div>
)
}
这个方案结合了:
- 基础内容的静态生成
- 用户身份相关的动态客户端渲染
- 每小时自动重新生成静态内容
6. 性能优化关键指标对比
通过WebPageTest测试同一项目:
| SSR版本 | SSG版本
首字节时间(TTFB) | 780ms | 58ms
完全可交互时间 | 2.3s | 1.8s
服务器成本 | $320/月 | $0(CDN)
构建时间 | - | 4分22秒
动态内容延迟 | 实时 | 最大60秒
7. 特殊场景处理指南
场景1:用户登录态管理
// _app.js中处理SSR鉴权
function App({ Component, pageProps, user }) {
return (
<UserContext.Provider value={user}>
<Component {...pageProps} />
</UserContext.Provider>
)
}
App.getInitialProps = async ({ ctx }) => {
// 服务端获取用户信息
const cookies = parseCookies(ctx.req)
const user = await getUserFromToken(cookies.token)
return { user }
}
场景2:大型电商站的商品列表
// 分页的ISR实现
export async function getStaticPaths() {
// 只生成前10页,后续页面按需生成
const paths = Array.from({ length: 10 }).map((_, i) => ({
params: { page: (i + 1).toString() }
}))
return { paths, fallback: 'blocking' }
}
export async function getStaticProps({ params }) {
const page = Number(params.page)
const products = await fetchProducts(page)
if (!products.length) {
return { notFound: true }
}
return {
props: { products },
revalidate: 600 // 10分钟后重新生成
}
}
8. 选择困难症救星流程
![决策流程图文字描述] 当面对新页面开发时:
- 是否需要实时数据? → 是 → 选SSR
- 是否完全静态内容? → 是 → 选SSG
- 是否混合型内容? → 是 → ISR混合模式
- 是否要求极致的加载速度? → 是 → SSG+CDN
- 是否有频繁更新需求? → 是 → SSR+缓存策略
9. 来自实战的血泪教训
在一次促销活动中,我们错误地为秒杀页面使用SSG:
- 00:00 活动开始
- 00:03 前端显示已售罄
- 00:05 实际库存才耗尽 问题出在: • 静态生成时预取了错误库存 • 重新构建时间间隔过长 解决方法:改用SSR获取实时库存,结合Edge缓存将TTFB控制在200ms内。
10. 面向未来的技术选择
随着React 18和新框架的演进: • Server Components将模糊SSR/SSG界限 • Edge Runtime带来更快的SSR响应 • 部分水合(Partial Hydration)提升SSG的交互性 可以预见,未来的最佳实践可能是:
- 默认使用静态生成
- 动态部分采用按需SSR
- 配合边缘网络实现近实时更新