ai对话面试问题
1.组件封装与交互管理
我按职责拆分了 Main、SideBar 和 MarkdownRenderer,并抽取 MessageRow,统一处理消息角色、正文和生成状态。
消息、会话以及发送操作通过 Context 共享,侧边栏负责会话管理,主区域根据状态展示聊天内容。
文本发送、回车发送、推荐问题和语音识别结果都进入同一个 onSent 流程,复用消息创建、请求和状态更新逻辑。
2.流式解析与渐进渲染
我使用 fetch 发起请求,通过 ReadableStream 持续读取响应,再用 TextDecoder 进行流式解码。
考虑到一次读取不一定包含完整的数据行,我维护 SSE 解析缓冲区,保留未完成的内容,拼接完整后再解析 JSON、提取回复正文。
正文进入独立的渲染缓冲区,大约每 50 毫秒输出一小段,追加到同一条 AI 消息中,实现网络接收与界面更新节奏解耦。
生成结束时输出剩余文本并清理定时器,用户停止生成时通过 AbortController 中断请求。
3.虚拟列表与滚动跟随
针对长会话中消息节点不断增加的问题,我在聊天区接入 react-virtuoso,通过 data 提供完整消息,通过 itemContent 渲染单条消息,并配置 overscan 缓冲,减少同时挂载的消息组件和 DOM 数量。
滚动方面,通过 atBottomStateChange 维护底部状态,再用 followOutput 控制跟随:用户在底部时跟随最新输出,向上查看历史时关闭跟随。
同时通过 ref 封装主动滚底方法,用于发送消息等交互。
4.语音输入与状态管理
我把 Web Speech API 封装成 useSpeechRecognition Hook,对外提供识别状态、实时转写、错误信息和开始停止操作。
交互上设计了 idle、recording、processing 三态:
- 开始识别后进入
recording,在onresult中区分临时结果和最终结果,实时更新预览; - 用户手动结束后进入
processing,等待识别结束,再通过回调提交最终文本。
识别文本显式传给统一的 onSent 方法,复用文本发送流程。
同时处理常见识别错误,并在 effect 清理时取消识别、移除回调。
5.Markdown 渲染与代码高亮
我封装了 MarkdownRenderer,让消息组件只负责传入正文,格式解析和展示规则集中在渲染组件中。
底层使用 ReactMarkdown,通过 remark-gfm 支持表格、任务列表和删除线等语法,通过 rehype-highlight 提供基于 highlight.js 的代码高亮,并引入主题 CSS 控制高亮样式。
展示结构通过 components 扩展,使不同消息使用统一的 Markdown 渲染规则。


