#每天一道AI面试题 | 字节前端面试真题《AI 对话这种高频流式返回场景,前端会遇到哪些性能问题?》

这题表面问性能,实际上在问你有没有真正做过流式对话产品
开头导语
表面上,它像是在问:“AI 对话高频流式返回,会有什么性能问题?”
但如果你一上来只说:
-
• 渲染次数太多 -
• 页面会卡 -
• 要做优化
这其实不太够。
因为 AI 对话场景里的性能问题,不只是一个“页面卡不卡”的前端八股题。它背后连着的是:
-
• 流式内容怎么更新 -
• 长消息怎么渲染 -
• 多条消息怎么管理 -
• markdown 和代码块怎么处理 -
• 用户一边看一边滚动时会不会抖 -
• 页面为什么没崩,但体验已经崩了
所以这题真正想听的,通常不是“你知道性能优化这四个字”。而是:你到底有没有做过那种内容一边返回、页面一边长、状态一边变、用户还一边操作的 AI 对话界面。
正文
一、先说结论:AI 对话的性能问题,很多不是“单次渲染太重”,而是“持续更新太频繁”
做普通前端的时候,很多性能问题都比较像“某一下很重”。
比如:
-
• 打开一个复杂页面 -
• 渲染一个很大的表格 -
• 首屏组件太多 -
• 一次性计算量过大
但 AI 对话场景不太一样。它更常见的问题是:每次更新都不一定重,但更新发生得太频繁。
你想一下流式返回的过程:
-
• 服务端不断吐新内容 -
• 前端不断接收片段 -
• 当前消息不断 append -
• 页面不断重新渲染 -
• 用户还可能正在滚动、选择文本、切换会话、点停止生成
这时候前端面对的就不是一个静态页面。而是一个持续变化中的界面。
所以 AI 对话产品的性能压力,很多时候不在“瞬时爆发”。而在“持续抖动”。
二、最常见的第一个问题:流式内容更新太频繁,导致高频重渲染
这是最典型的。
如果后端每吐一点内容,前端就立刻 setState 一次。那页面就会进入一种很忙的状态:
-
• 文本不断变长 -
• React/Vue 不断触发更新 -
• 相关子组件跟着反复 render -
• 滚动区域不断重新计算
如果你的消息列表结构再复杂一点,问题会更明显。
比如一条消息里还包含:
-
• markdown 解析 -
• 代码高亮 -
• 数学公式 -
• 表格 -
• 引用来源 -
• 工具执行状态
那每次流片段进来,代价就不只是“多几个字”。而是整条消息相关的渲染链路可能都要重新走一遍。
所以第一类性能问题,往往不是“内容大”。而是:内容在持续增长,而你每增长一点就让整棵树跟着忙一次。
三、第二个问题:长消息越来越长,渲染成本会越来越高
AI 对话和普通聊天的一个很大区别是:一条消息可能特别长。
普通 IM 里,一条消息大多就一两句话。AI 对话里,一条回答可能是:
-
• 几百字 -
• 几千字 -
• 带代码 -
• 带表格 -
• 带多级标题 -
• 甚至像一篇小文档
这意味着什么?
意味着随着流式返回继续,当前这条消息的渲染成本会越来越高。不是固定成本。而是增长成本。
你一开始 append 10 个字,也许没感觉。但等消息长到几千字以后,你再每次追加一点点,都可能触发更重的重排、重绘和组件更新。
所以 AI 对话很容易出现一种现象:开头挺顺,越往后越卡。
这不是错觉。很多时候真的是因为那条消息已经被喂得太大了。
四、第三个问题:markdown、代码高亮、富文本解析会把压力继续放大
很多 AI 对话产品为了更好看,都会把回答按富文本来展示。
比如支持:
-
• markdown -
• code block -
• syntax highlight -
• 表格 -
• 引用块 -
• 列表 -
• LaTeX
这当然能提高可读性。但也会把性能问题放大。
因为这时前端不只是“显示文本”。而是在做:
-
• 文本切分 -
• markdown 解析 -
• AST 构建 -
• 代码高亮 -
• DOM 结构重建
如果你每收到一小段流式内容,就把整段全文重新 parse 一遍,性能很容易出问题。
尤其是代码块场景。因为高亮往往并不便宜。
所以很多时候真正卡的,不是“流式”本身。而是:流式内容驱动了高成本富文本链路的高频重复执行。
五、第四个问题:消息列表变长后,整个对话区域也会越来越重
很多人只盯着“当前正在生成的那条消息”。但其实整个聊天列表也会变成压力源。
因为 AI 对话不是只看最后一条。用户会往上翻。会切回历史。会复制代码。会对比前后回答。
如果页面里已经堆了很多轮对话:
-
• 每轮都很长 -
• 每轮都有 markdown -
• 每轮都有代码块 -
• 每轮可能还有引用和工具状态
那整个消息列表本身就会变得很重。
这时候问题可能表现成:
-
• 滚动掉帧 -
• 滚动时白屏一下 -
• 定位到底部不稳定 -
• 自动滚动和手动滚动打架 -
• 切会话变慢
所以第四类性能问题,不是单条消息,而是:整个聊天容器随着历史变长,逐渐失去轻盈感。
六、第五个问题:滚动联动很容易出体验型性能问题
AI 对话页面里,滚动几乎永远不是一个简单动作。
因为它经常和这些东西绑在一起:
-
• 新内容不断追加 -
• 自动滚到底部 -
• 用户正在手动往上翻 -
• 用户选中文本 -
• 工具结果区域展开/折叠
这时候最容易出一种很烦的 bug:性能不一定真的炸了,但体验已经抖起来了。
比如:
-
• 你刚往上翻,页面又把你拽回底部 -
• 你正看上一段,新增内容导致布局跳动 -
• 你在复制代码,结果流式更新把位置顶掉 -
• 页面一直在细小抖动,用户说不上哪不对,但就是难受
所以 AI 对话的性能,很多时候不是 CPU 跑满那种“硬卡”。而是交互层面的“软卡”。
页面还活着。但人已经烦了。
七、第六个问题:状态拆得不好,会把性能问题和状态问题搅成一锅
这点特别常见。
如果你的消息状态、流式状态、会话状态、加载状态、错误状态,全都混在一个很大的对象里。那流式内容每更新一次,可能影响的不只是当前那条消息。
它还可能导致:
-
• 整个会话区域一起重渲染 -
• 顶部 header 跟着刷 -
• 输入框状态也被带着变 -
• 不该更新的组件也一起跑了
这时候你会发现:前端看起来是在处理“流式返回性能问题”。其实根子上是:状态边界没切清楚。
所以这题再往深一点,其实还在问:你有没有把 AI 对话页面当成一个“持续变化的状态系统”来设计。
八、面试里如果被问到“怎么优化”,别只说 memo
很多人一到这里就开始背:
-
• memo -
• useMemo -
• useCallback -
• 防抖节流
这些不是没用。但如果只停在这里,味道还是浅。
更像做过项目的答法,通常会从几个层次讲:
1)降低流式更新频率
不是每来一个 token 就立刻刷一次页面。可以做合并、缓冲、按时间片更新。
核心思想是:不要让 UI 更新频率完全等于服务端吐流频率。
2)把“正在生成的消息”和“历史消息”隔离
历史消息尽量稳定,不要跟着当前流一起反复更新。否则整个列表会被拖着跑。
3)富文本链路做分阶段处理
比如:
-
• 先渲染纯文本 -
• markdown 延后解析 -
• 代码高亮按块处理 -
• 重内容区域懒执行
不要在最频繁更新的时候,把最贵的解析链路全打开。
4)长列表要考虑虚拟化或折叠策略
尤其是历史很长、内容很多的时候。否则聊天窗口会越来越重。
5)滚动策略要明确
什么时候自动滚到底部,什么时候尊重用户手动浏览,什么时候暂停联动。这不只是交互设计,也直接影响性能体验。
6)状态边界拆清楚
把当前消息流、消息列表、会话级状态、输入区状态拆开。别让一次 append 搅动整页。
这时候你的回答,就不是“知道几个优化词”。而像“真的踩过坑”。
九、面试里可以怎么组织成一段顺口回答?
你可以这么答:
AI 对话这种高频流式返回场景,前端最典型的性能问题不是单次渲染重,而是持续更新太频繁。每次流式内容进来都会触发当前消息增长、组件重渲染、滚动区域更新,如果再叠加 markdown 解析、代码高亮、长消息渲染和长列表滚动,性能问题会被进一步放大。
然后接着补:
所以优化思路通常不是单点 memo,而是从更新频率控制、当前消息和历史消息隔离、富文本分阶段处理、长列表虚拟化、滚动策略和状态拆分这几个层面一起做。
最后再收一句:
这类场景最怕的不是页面瞬间卡死,而是页面一直在小幅抖动、频繁重算,最后把体验慢慢磨坏。
夜雨聆风