共计 2402 个字符,预计需要花费 7 分钟才能阅读完成。
核心概念解析
ChatGPT 式界面的核心在于营造自然对话体验,主要包含三个技术特征:

- 消息流式渲染 :服务端采用分块传输(chunked encoding),前端需要处理分段到达的数据并实时更新界面
- 打字机效果 :通过控制字符逐个显示的速度,模拟人类打字节奏(建议 15-30ms/ 字符)
- 会话持久化 :需要维护对话上下文,包括本地存储和跨页面状态保持
技术选型对比
实时通信方案选择直接影响用户体验和开发复杂度:
- WebSocket:双向通信首选,但需要额外维护连接状态
- SSE(Server-Sent Events):单向通信更轻量,自动处理重连,适合消息推送场景
- 长轮询 :兼容性好但延迟高,不推荐生产环境使用
// 典型 SSE 连接示例
const eventSource = new EventSource('/api/chat');
eventSource.onmessage = (event) => {setMessages(prev => [...prev, JSON.parse(event.data)]);
};
核心代码实现
1. 状态管理架构
建议使用 Context API + useReducer 管理复杂对话状态:
// 对话状态类型定义
type Message = {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: number;
};
// 状态 reducer
function chatReducer(state: Message[], action: {
type: 'ADD' | 'UPDATE' | 'CLEAR';
payload?: Message | string;
}) {// ... 不可变状态更新逻辑}
2. 打字机效果实现
关键点在于使用递归 setTimeout 控制渲染节奏:
function Typewriter({text, speed = 20}: {
text: string;
speed?: number;
}) {const [displayed, setDisplayed] = useState('');
useEffect(() => {
let index = 0;
const timer = setInterval(() => {if (index < text.length) {setDisplayed(prev => prev + text.charAt(index));
index++;
} else {clearInterval(timer);
}
}, speed);
return () => clearInterval(timer);
}, [text]);
return <span>{displayed}</span>;
}
3. 本地持久化方案
// 保存会话到 localStorage
const saveSession = (messages: Message[]) => {
try {localStorage.setItem('chat_history', JSON.stringify(messages));
} catch (error) {console.error('存储失败:', error);
}
};
// 初始化时读取
useEffect(() => {const saved = localStorage.getItem('chat_history');
if (saved) dispatch({type: 'LOAD', payload: JSON.parse(saved) });
}, []);
性能优化策略
虚拟滚动实现
使用 react-window 处理长列表:
import {FixedSizeList as List} from 'react-window';
const MessageList = ({messages}: {messages: Message[] }) => (
<List
height={600}
itemCount={messages.length}
itemSize={80}
width="100%"
>
{({index, style}) => (<div style={style}>
<MessageBubble message={messages[index]} />
</div>
)}
</List>
);
请求防抖处理
const debouncedSend = useMemo(() => debounce(sendMessage, 500),
[sendMessage]
);
常见问题解决方案
SSE 自动重连机制
function useSSE(url: string, callback: (data: string) => void) {useEffect(() => {
let es: EventSource;
const connect = () => {es = new EventSource(url);
es.onmessage = (e) => callback(e.data);
es.onerror = () => {es.close();
setTimeout(connect, 1000); // 1 秒后重连
};
};
connect();
return () => es.close();
}, [url]);
}
移动端输入优化
/* 防止键盘弹出挤压界面 */
.input-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: env(safe-area-inset-bottom);
}
延伸思考
- 如何实现图片 / 代码块等多模态消息的渲染?
- 当对话出现分支时(如用户点击修改历史消息),如何设计状态管理?
- 在弱网环境下,有哪些方案可以保证消息的可靠投递?
实践建议
建议从简单版本开始迭代:
1. 先实现基础消息收发
2. 添加打字机动画
3. 引入持久化存储
4. 最后进行性能优化
遇到样式问题时,可以使用 ChatGPT 官网的 CSS 作为参考(通过开发者工具查看),但注意不要直接复制商业代码。调试阶段推荐使用 Mock Service Worker 模拟 API 响应,避免频繁调用真实接口。
正文完
