共计 2727 个字符,预计需要花费 7 分钟才能阅读完成。
背景与痛点
对话式 AI 前端界面与传统网页应用存在显著差异,主要体现在以下几个方面:

- 实时性要求高 :消息需要即时显示,且要保持流畅的交互体验
- 消息管理复杂 :需要处理消息历史、状态同步、撤回等场景
- 内容渲染特殊 :支持 Markdown 语法、代码高亮、数学公式等专业格式
- 性能挑战大 :长对话场景下需保持滚动流畅,避免内存泄漏
技术选型
框架对比
- React
- 优势:成熟的生态系统、强大的状态管理方案、优秀的虚拟 DOM 性能
-
适合场景:复杂交互、需要精细控制渲染流程
-
Vue
- 优势:更简单的学习曲线、内置的过渡动画系统
-
不足:在超长列表渲染方面稍逊于 React
-
Svelte
- 优势:无虚拟 DOM、编译时优化
- 不足:生态相对较小,调试工具不够完善
最终选择 React + TypeScript 组合,原因如下:
- TypeScript 能有效管理复杂的状态类型
- React 的虚拟 DOM 更适合消息列表的优化渲染
- 社区资源丰富,遇到问题容易找到解决方案
核心实现
1. 实时对话渲染机制
采用虚拟滚动技术优化长列表性能:
interface Message {
id: string;
content: string;
role: 'user' | 'assistant';
timestamp: number;
}
const VirtualizedList = ({messages}: {messages: Message[] }) => {const parentRef = useRef<HTMLDivElement>(null);
// 使用 react-window 实现虚拟滚动
return (<div ref={parentRef} style={{height: '500px', overflow: 'auto'}}>
<FixedSizeList
height={500}
itemCount={messages.length}
itemSize={100}
width={'100%'}
>
{({index, style}) => (<div style={style}>
<MessageItem message={messages[index]} />
</div>
)}
</FixedSizeList>
</div>
);
};
2. 消息状态管理
对比两种方案后选择 Context API + useReducer:
- Redux:适合大型应用,但引入额外复杂度
- Context API:内置方案,配合 useReducer 足够应对当前场景
// 定义状态类型
type ChatState = {messages: Message[];
isLoading: boolean;
error: string | null;
};
// 创建 Context
const ChatContext = createContext<{
state: ChatState;
dispatch: Dispatch<ChatAction>;
} | null>(null);
// 使用 Provider 包装
const ChatProvider = ({children}: {children: ReactNode}) => {const [state, dispatch] = useReducer(chatReducer, initialState);
return (<ChatContext.Provider value={{ state, dispatch}}>
{children}
</ChatContext.Provider>
);
};
3. Markdown 解析与代码高亮
组合使用 remark 和 prism.js:
import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
const MarkdownRenderer = ({content}: {content: string}) => {const [html, setHtml] = useState('');
useEffect(() => {unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeHighlight)
.use(rehypeStringify)
.process(content)
.then((file) => setHtml(String(file)));
}, [content]);
return <div dangerouslySetInnerHTML={{__html: html}} />;
};
4. 打字机效果实现
使用 CSS 动画配合 JavaScript 控制:
const Typewriter = ({text}: {text: string}) => {const [displayedText, setDisplayedText] = useState('');
useEffect(() => {
let i = 0;
const timer = setInterval(() => {if (i < text.length) {setDisplayedText(text.substring(0, i + 1));
i++;
} else {clearInterval(timer);
}
}, 50); // 控制打字速度
return () => clearInterval(timer);
}, [text]);
return <span>{displayedText}</span>;
};
性能优化
消息列表渲染测试
| 消息数量 | 普通渲染 (ms) | 虚拟滚动 (ms) |
|---|---|---|
| 100 | 120 | 45 |
| 500 | 580 | 60 |
| 1000 | 内存警告 | 85 |
内存泄漏预防
- 清除所有定时器和事件监听器
- 避免在 useEffect 中直接修改状态
- 使用 React Developer Tools 检测组件卸载情况
避坑指南
长对话性能问题
- 实现消息分页加载
- 对过旧的消息进行归档
- 禁用非可见区域的动画
移动端适配
- 使用 viewport 单位
- 优化虚拟滚动项高度
- 增加触摸反馈延迟
扩展思考
语音输入集成
- 使用 Web Speech API 获取语音输入
- 添加可视化声波反馈
- 实现语音识别状态管理
多模态支持
- 扩展 Message 类型支持图片 / 视频
- 实现文件上传预览
- 优化混合内容布局
完整实现可参考:CodeSandbox 示例
总结
构建类 ChatGPT 界面需要综合考虑实时性、渲染性能和用户体验。React 配合 TypeScript 提供了良好的开发体验,而虚拟滚动、合理的状态管理和 Markdown 解析是实现核心功能的关键。性能优化需要持续关注,特别是在长对话场景下。未来可以考虑加入更多交互形式,打造更丰富的对话体验。
正文完
发表至: 前端开发
近一天内
