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>
  )
}

运行效果变化:

  1. 请求到达Node服务器
  2. 服务器执行getServerSideProps
  3. 生成完整HTML返回
  4. 浏览器立即显示完整内容

实施后首屏加载时间缩短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>
  )
}

这个方案结合了:

  1. 基础内容的静态生成
  2. 用户身份相关的动态客户端渲染
  3. 每小时自动重新生成静态内容

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. 选择困难症救星流程

![决策流程图文字描述] 当面对新页面开发时:

  1. 是否需要实时数据? → 是 → 选SSR
  2. 是否完全静态内容? → 是 → 选SSG
  3. 是否混合型内容? → 是 → ISR混合模式
  4. 是否要求极致的加载速度? → 是 → SSG+CDN
  5. 是否有频繁更新需求? → 是 → 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
  • 配合边缘网络实现近实时更新