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

NextJS 15中SSR与CSR组件的条件渲染实现方案咨询

解决方案:Next.js 15 中混合 SSR/CSR 组件实现 ChatBot 布局切换

核心思路

将静态/无需交互的内容作为服务端组件(SSR)渲染,保障首屏加载性能;将需要状态交互的模块(如输入框、布局控制逻辑)标记为客户端组件(CSR),通过客户端状态实现条件渲染。服务端组件无法使用useState,但可通过引入客户端组件承载交互逻辑,实现两者的协同工作。

步骤1:拆分组件职责

  • 服务端组件:负责渲染无交互需求的静态内容(如品牌标识、欢迎文案),直接在服务端生成HTML,提升首屏速度。
  • 客户端组件:承载聊天输入框、消息列表、状态管理逻辑,顶部添加'use client'指令以启用React客户端API(如useState)。

步骤2:客户端组件实现状态控制

在客户端组件中维护hasSentFirstMessage状态,以此触发布局切换:

// app/components/ChatContainer.tsx
'use client';

import { useState, useRef } from 'react';

export default function ChatContainer() {
  const [hasSentFirstMessage, setHasSentFirstMessage] = useState(false);
  const [messages, setMessages] = useState<string[]>([]);
  const inputRef = useRef<HTMLInputElement>(null);

  const handleSendMessage = () => {
    if (!inputRef.current?.value.trim()) return;
    
    // 新增消息到列表
    setMessages(prev => [...prev, inputRef.current.value]);
    inputRef.current.value = '';
    
    // 标记已发送第一条消息
    if (!hasSentFirstMessage) {
      setHasSentFirstMessage(true);
    }
  };

  return (
    <div className={`chat-container ${hasSentFirstMessage ? 'active' : ''}`}>
      {/* 初始状态显示的周边组件(仅未发送消息时渲染) */}
      {!hasSentFirstMessage && (
        <div className="welcome-panel">
          <h2>欢迎使用ChatBot</h2>
          <p>发送第一条消息开启对话</p>
        </div>
      )}

      {/* 消息列表(发送消息后显示) */}
      {hasSentFirstMessage && (
        <div className="messages-list">
          {messages.map((msg, idx) => (
            <div key={idx} className="message-item">{msg}</div>
          ))}
        </div>
      )}

      {/* 输入框区域,根据状态切换位置 */}
      <div className={`input-group ${hasSentFirstMessage ? 'fixed-bottom' : 'centered'}`}>
        <input ref={inputRef} type="text" placeholder="输入消息..." />
        <button onClick={handleSendMessage}>发送</button>
      </div>
    </div>
  );
}

步骤3:服务端页面组件组合

页面组件作为服务端组件,直接引入客户端ChatContainer,同时可添加其他SSR内容:

// app/page.tsx
import ChatContainer from './components/ChatContainer';
import PageFooter from './components/PageFooter'; // 假设为服务端组件

export default function Home() {
  return (
    <div className="page-wrapper">
      <header className="page-header">
        {/* SSR渲染的头部内容 */}
        <h1>My ChatBot</h1>
      </header>
      <ChatContainer />
      <PageFooter />
    </div>
  );
}

步骤4:样式实现布局切换

通过CSS类名切换控制布局(示例使用原生CSS,也可替换为Tailwind或CSS模块):

/* app/globals.css */
.chat-container {
  width: 100%;
  height: 80vh;
  position: relative;
  overflow: hidden;
}

.welcome-panel {
  text-align: center;
  padding: 3rem 1rem;
}

.messages-list {
  height: calc(100% - 80px);
  overflow-y: auto;
  padding: 1rem;
  border-top: 1px solid #eee;
}

.input-group.centered {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80%;
  display: flex;
  gap: 0.5rem;
}

.input-group.fixed-bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 1rem;
  display: flex;
  gap: 0.5rem;
  border-top: 1px solid #eee;
  background: #fff;
}

关键注意事项

  • 客户端组件必须添加'use client'指令,否则无法使用useState等客户端API。
  • 服务端组件可直接引入客户端组件,但客户端组件不能直接引入服务端组件(需通过props传递服务端数据)。
  • 状态仅在客户端维护,不会影响服务端渲染的初始内容,兼顾首屏性能与交互体验。

内容的提问来源于stack exchange,提问作者TreyCollier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:20:54