You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将客户端渲染的React商品容器组件转为SSR?

将CSR组件转为SSR的非CSS实现方案

问题背景

我想了解是否存在将以下代码从客户端渲染(CSR)转为服务端渲染(SSR)的方法:

'use client'
import React, { useState, useEffect } from 'react'
import DesktopProduct from './DesktopProduct'
import MobileProduct from './MobileProduct'
import { useGetProductQuery } from '@/redux/services/productsFetch'

const ProductContainer = ({ id }: { id: string }) => {
  const [mobile, setMobile] = useState(false)
  const [desktop, setDesktop] = useState(false)

  const { data } = useGetProductQuery(id)

  useEffect(() => {
    if (window.innerWidth <= 720) {
      setMobile(true)
    } else {
      setDesktop(true)
    }
    addEventListener('resize', () => {
      if (window.innerWidth <= 720) {
        setMobile(true)
        setDesktop(false)
      } else {
        setDesktop(true)
        setMobile(false)
      }
    })
  }, [])
  return (
    <div>
      {mobile && data && <MobileProduct data={data} />}
      {desktop && data && <DesktopProduct data={data} />}
    </div>
  )
}

该应用的移动端与桌面端页面完全不同,内部组件无需处理任何仅客户端可处理的数据,因此从性能角度考虑,采用SSR会更优。我已尝试多种方法,其中调整页面CSS的方案已生效,但想了解除CSS外的其他实现方式。

可行方案

1. 基于User-Agent的服务端组件选择

直接在服务端解析请求的User-Agent头部判断设备类型,返回对应组件的渲染结果,同时在服务端完成数据获取:

// Next.js App Router 示例
import { headers } from 'next/headers'
import DesktopProduct from './DesktopProduct'
import MobileProduct from './MobileProduct'
import { getProduct } from '@/redux/services/productsFetch' // 改用服务端可调用的API方法

export default async function ProductContainer({ params }: { params: { id: string } }) {
  const userAgent = headers().get('user-agent') || ''
  const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)
  
  const productData = await getProduct(params.id)

  return isMobile ? <MobileProduct data={productData} /> : <DesktopProduct data={productData} />
}
  • 优势:首屏渲染速度最快,完全避免客户端数据请求和初始状态切换
  • 局限性:User-Agent判断可能存在误差,无法响应客户端窗口大小变化(需配合客户端重定向或动态切换逻辑)

2. 服务端预渲染+客户端动态切换

服务端预渲染两个组件的内容,客户端仅根据窗口大小控制显示/隐藏:

// 移除'use client'指令,服务端获取数据
import { getProduct } from '@/redux/services/productsFetch'
import DesktopProduct from './DesktopProduct'
import MobileProduct from './MobileProduct'
import { useEffect, useState } from 'react'

export default async function ProductContainer({ params }: { params: { id: string } }) {
  const productData = await getProduct(params.id)
  const [isMobile, setIsMobile] = useState(false)

  useEffect(() => {
    const checkScreenSize = () => setIsMobile(window.innerWidth <= 720)
    // 初始检测
    checkScreenSize()
    // 监听窗口变化
    window.addEventListener('resize', checkScreenSize)
    return () => window.removeEventListener('resize', checkScreenSize)
  }, [])

  return (
    <div>
      {isMobile ? <MobileProduct data={productData} /> : <DesktopProduct data={productData} />}
    </div>
  )
}
  • 优势:服务端完成数据获取和组件预渲染,首屏有内容,同时支持窗口大小动态调整
  • 注意事项:确保两个组件的服务端渲染不会产生DOM冲突,客户端会快速切换到正确视图

3. 动态导入服务端组件+客户端切换控制

将移动端/桌面端组件设为服务端组件,客户端负责窗口检测和组件加载逻辑:

'use client'
import { useEffect, useState } from 'react'
import dynamic from 'next/dynamic'

// 动态导入服务端组件
const DesktopProduct = dynamic(() => import('./DesktopProduct'), { ssr: true })
const MobileProduct = dynamic(() => import('./MobileProduct'), { ssr: true })

// 服务端数据获取Wrapper
async function ProductDataProvider({ id, children }: { id: string; children: (data: any) => React.ReactNode }) {
  const productData = await getProduct(id)
  return children(productData)
}

export default function ProductContainer({ id }: { id: string }) {
  const [isMobile, setIsMobile] = useState(false)

  useEffect(() => {
    const checkScreenSize = () => setIsMobile(window.innerWidth <= 720)
    checkScreenSize()
    window.addEventListener('resize', checkScreenSize)
    return () => window.removeEventListener('resize', checkScreenSize)
  }, [])

  return (
    <ProductDataProvider id={id}>
      {(data) => isMobile ? <MobileProduct data={data} /> : <DesktopProduct data={data} />}
    </ProductDataProvider>
  )
}
  • 优势:数据在服务端获取,组件按需加载,兼顾SSR性能和客户端动态切换需求
  • 注意事项:动态导入的组件需确保兼容服务端渲染,避免使用客户端专属API

内容的提问来源于stack exchange,提问作者Luis David Onate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:43:17