用React+TypeScript+Tailwind快速搭建带打字状态、历史记录的聊天UI:创建项目→配置Tailwind→三栏布局→消息气泡左右对齐→接入OpenAI流式API→localStorage持久化会话。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

你需要在30分钟内上线一个能收发消息、带打字状态、支持历史记录的实时聊天界面,而不是从零写WebSocket或部署后端服务。
用React+Tailwind快速搭出聊天UI骨架
打开终端,执行 npx create-react-app chat-ui --template typescript 创建新项目。这一步必须用TypeScript模板,因为后续要对接OpenAI API的类型定义,否则会频繁遇到any类型报错。
进入项目目录后,运行 npm install -D tailwindcss postcss autoprefixer → npx tailwindcss init -p → 在tailwind.config.js中配置content路径包含src/**/*.{js,jsx,ts,tsx}。
把src/App.css清空,替换为@tailwind base; @tailwind components; @tailwind utilities;。这一步漏掉会导致所有Tailwind类名失效,页面变成纯白。
在src/App.tsx中,用Flex布局构建三栏结构:左侧会话列表(固定宽240px)、中间消息区(flex-1)、右侧参数面板(宽320px)。消息气泡用bg-blue-500 text-white rounded-2xl px-4 py-2 max-w-[80%]实现,用户消息对齐右侧,AI消息对齐左侧——【左右对齐必须用ml-auto和mr-auto控制,不能只靠text-right,否则长消息会溢出容器】。
接入OpenAI API模拟实时流式响应
方法一:使用openai官方SDK(推荐)
执行 npm install openai。在src/lib/aiClient.ts中创建客户端实例,【API密钥必须通过环境变量import.meta.env.VITE_OPENAI_API_KEY注入,绝不能硬编码在JS文件里】。
用于在用户想通过浏览器自动化与 Google Gemini 或 ChatGPT 交互时。触发短语包括“ask Gemini”“ask ChatGPT”“ask GPT”“让...”。
调用createChatCompletion时,必须传入stream: true选项,并在onChunk回调里逐帧拼接文本。注意:流式响应的delta.content字段可能为空字符串,需过滤掉,否则界面上会出现闪烁空白。
方法二:用fetch直连API(适合调试)
构造POST请求到https://api.openai.com/v1/chat/completions,Header中设置Authorization: Bearer ${apiKey}和Content-Type: application/json。Body中messages数组必须包含role: "user"和role: "system"两条,否则模型会拒绝响应。
实现打字状态与消息历史持久化
第一步:在useState中声明isTyping布尔值,当用户点击发送后立即设为true,收到完整响应后设为false。
第二步:在消息气泡下方添加打字指示器——用isTyping && <div classname="flex space-x-1 ml-2">
<div classname="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
<div classname="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="{{animationDelay:"></div>
<div classname="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="{{animationDelay:"></div>
</div>。这个三圆点动画必须用animate-bounce配合不同animationDelay,用opacity-0切换会卡顿。
第三步:用localStorage保存conversations数组,每次新会话生成唯一id(可用Date.now().toString(36)),存入时用JSON.stringify序列化。读取时用try/catch包裹JSON.parse,防止因手动编辑localStorage导致解析失败崩溃。
第四步:在组件卸载前(useEffect清理函数),将当前会话的messages数组更新到localStorage对应id下。这一步不做,刷新页面后消息就消失了。

















