前端状态、性能与恢复

共 27 题
📑 题目列表 27 题
#
★★★

1. 长会话如何用虚拟列表和稳定锚点渲染,流式增长时怎样避免滚动跳动

长会话如何用虚拟列表和稳定锚点渲染,流式增长时怎样避免滚动跳动?

  • 虚拟列表的渲染范围控制
  • 稳定锚点(scroll anchoring)机制
  • 流式增长时保持滚动位置

长会话用虚拟列表只渲染可视区 + 缓冲区的消息,避免大量 DOM。防滚动跳动:消息节点用稳定的 key 和固定/可预测的占位高度,使用 CSS overflow-anchor 或手动 scroll anchoring 锚定到当前可见消息;流式增长时新内容追加在底部或顶部,若用户在看历史则锚定当前消息,若停在底部则自动跟随最新内容。用 IntersectionObserver 感知"是否在底部",配合 scrollIntoView 或直接调整 scrollTop 保持位置。

滚动跳动来自内容增长导致布局偏移。虚拟列表减少 DOM 规模,锚定机制保证追加内容时当前视口不跳。区分"用户是否在看底部"决定是否自动滚动,是流式聊天 UX 的关键。

#
★★★

2. 如何用 rAF/时间片节流增量更新,同时保证停止按钮、输入框和页面滚动响应

如何用 rAF/时间片节流增量更新,同时保证停止按钮、输入框和页面滚动响应?

  • requestAnimationFrame 与时间片调度
  • 增量更新与主线程阻塞控制
  • 关键交互(停止按钮、输入、滚动)的响应性

流式 token 到达时不立即同步渲染,而是缓存到缓冲区,用 requestAnimationFrame 或时间片(如每 16ms 或每 N 个 token 合并一次)批量更新 DOM,避免每 token 都触发重渲染阻塞主线程。长时间任务拆成可中断的片段,预留时间给输入框、滚动和停止按钮的事件处理。用 isInputPending 或分批刷新让关键交互优先。停止按钮的事件处理放在独立高优先级路径,随时可中断渲染循环。

主线程被渲染占满会卡住输入、滚动和停止按钮。rAF/时间片合并批量更新 + 让出主线程 + 关键交互优先,是保持界面响应与流式流畅的平衡。停止按钮必须能立即响应,不能排在渲染循环之后。

#
★★★

3. AbortController 能取消哪些浏览器工作,服务端模型推理为何还需要独立取消协议

AbortController 能取消哪些浏览器工作?服务端模型推理为何还需要独立取消协议?

  • AbortController 的取消范围(fetch、SSE、无网络计算)
  • 浏览器侧取消的局限
  • 服务端独立取消协议的必要性

AbortController 能取消浏览器侧的 fetch 请求、SSE/EventSource、WebSocket 连接、以及某些支持 signal 的异步操作(如 ReadableStream 读取)。它取消的是"浏览器发起的连接",一旦请求已发出,服务端是否继续处理、是否继续计费,浏览器无法控制。因此服务端模型推理需要独立取消协议:BFF 通过请求 id 调用上游 Provider 的取消接口,或通过流协议发送结束信号,才能真正停止推理与计费。否则前端 abort 只是断开连接,Provider 可能继续跑完并计入费用。

AbortController 解决"客户端不再接收",但"服务端停止处理"是另一回事。模型推理昂贵,若前端取消后服务端仍跑完,会产生浪费与计费问题,故必须有服务端取消协议与之配合。

#
★★★

4. 流式聊天中“停止生成”后如何清理未完成的 DOM 节点、撤销已渲染的占位符

流式聊天中"停止生成"后,如何清理未完成的 DOM 节点、撤销已渲染的占位符?

  • 未完成内容与占位符的清理
  • 已渲染半截结构的处理
  • 状态与 DOM 的一致性

停止生成后:先取消底层请求(AbortController / 服务端取消协议),然后清理未完成的 DOM 节点——结束光标/动画占位符、移除未闭合的临时标记、把当前半截内容封口为完整可读状态(或标记为"已停止")。撤销已渲染的占位符:工具进度卡片若未完成则显示"已取消",流式占位符删除。所有清理应通过状态机统一走"stopped"状态,保证 DOM 与数据一致,避免残留幽灵节点。

停止不是简单清空,而是"把已渲染内容收尾为一致状态"。占位符、光标、未闭合结构都需要显式清理,否则产生残留 DOM 与状态不一致。用状态机驱动清理保证一致性。

#
★★★

5. 消息列表的虚拟滚动应如何处理变高项(带代码块、表格、图片)防止布局抖动

消息列表的虚拟滚动应如何处理变高项(带代码块、表格、图片)防止布局抖动?

  • 变高项的尺寸测量与缓存
  • 虚拟滚动的定位算法
  • 防布局抖动策略

变高项(代码块、表格、图片)不能用固定高度估算,需在渲染后测量实际高度并缓存到映射表(id → 高度),虚拟滚动用缓存高度计算偏移量;对未测量项用占位高度估算。图片在加载后高度变化需重新测量并触发重排。可使用"estimated height + 动态校正"算法,或分片渲染避免一次性测量大量项。内容变化(如流式增长)时需更新对应项高度并重算偏移。

固定高度的虚拟列表对变高项会错位。策略是"先估后测 + 缓存高度 + 动态校正",图片等异步资源加载后重新测量。这减少布局抖动,提升滚动稳定性。

#
★★★

6. IndexedDB 如何持久化允许落地的会话状态,刷新后怎样与服务端版本合并

IndexedDB 如何持久化允许落地的会话状态,刷新后怎样与服务端版本合并?

  • IndexedDB 持久化会话的数据结构
  • 刷新后数据的恢复
  • 与服务端版本的合并策略

IndexedDB 持久化会话树、消息、附件元数据、引用、版本号与游标。刷新启动时先读本地缓存立即渲染,再异步拉取服务端权威版本,按版本号合并:以服务端为准覆盖已落地的数据,本地未提交的编辑(pending)保留并与服务端合并后回写。用 last-write-wins 或版本号比较,冲突字段标记待用户确认。合并后回写 IndexedDB 保持缓存一致。

IndexedDB 用于"立即恢复 + 离线缓存",但权威在服务端。刷新后先本地后服务端,配合版本号合并,既保证首屏即时又保证一致性。未提交编辑需保留,不能因刷新丢失。

#
★★★

7. 断线恢复如何使用游标、事件 ID 和去重;旧流晚到时如何防止串写新消息

断线恢复如何使用游标、事件 ID 和去重?旧流晚到时如何防止串写新消息?

  • 游标与事件 ID 的断点续传
  • 事件去重机制
  • 旧流晚到与并发写入隔离

断线恢复用游标(cursor)记录最近一条已处理事件的位置,重连后从游标继续拉取;每条流事件带唯一事件 ID(如递增序列号),前端按序列号去重,避免重复 token 或重复消息。防止旧流晚到串写新消息:每个请求流绑定独立 requestId,写入时校验该 requestId 是否为当前活动流,旧流(已被新生成替代)的事件直接丢弃,不写入消息树。用消息版本号/游标校验保证写入顺序。

断线重连 + 多流并发(用户中途重新生成)会产生乱序与重复。游标 + 事件 ID 解决续传与去重,requestId 隔离解决旧流晚到串写。这些是 SSE 可靠性的核心。

#
★★★

8. 大附件如何做分片、Hash、进度、重试和 MIME 校验,Web Worker 适合卸载哪些工作

大附件如何做分片、Hash、进度、重试和 MIME 校验?Web Worker 适合卸载哪些工作?

  • 分片上传、Hash 计算与完整性校验
  • 进度与断点重试
  • Web Worker 卸载的适合任务

大附件上传:把文件切成固定大小分片(如 4-8MB),为每片计算 Hash(如 SHA-256),服务端校验后合并;客户端算整体/分片 Hash 供完整性校验,支持断点续传(重连后从已确认的分片继续)。进度用已上传分片数/总字节计算,失败分片重试。MIME 校验:用文件头(magic bytes)而非扩展名判断真实类型。Web Worker 适合卸载重计算且不依赖 DOM 的任务:Hash 计算、大文件分片、Markdown 解析、JSON Schema 校验、向量计算等,避免阻塞主线程。

大附件核心是"分片 + 校验 + 可恢复"。Hash 计算是 CPU 密集,放 Worker 避免卡 UI;真实 MIME 检测防绕过。Web Worker 卸载的是计算型任务,不能访问 DOM。

#
★★★

9. IndexedDB 存储哪些会话数据(消息、附件、引用),如何加密与同步到服务端

IndexedDB 存储哪些会话数据(消息、附件、引用),如何加密与同步到服务端?

  • IndexedDB 存储的数据类型
  • 加密方案(IndexedDB 本身不加密)
  • 与服务端的同步策略

IndexedDB 存储会话消息、附件元数据(非原始文件,或小文件)、引用、模型/工具配置、版本号与游标。IndexedDB 本身不加密,敏感数据需用 Web Crypto API 在应用层加密(如 AES-GCM,密钥可来自 OIDC 派生或服务端托管),再写入。同步:本地缓存与权威服务端用版本号/游标增量同步,本地变更上传、服务端变更拉取,冲突合并。附件大文件存对象存储,IndexedDB 只存元数据与索引。

IndexedDB 是明文存储,安全敏感会话数据必须应用层加密。同步要增量(版本号 + 游标)避免全量传输。附件大文件不能塞进 IndexedDB,应存远端对象存储。

#
★★

10. 聊天应用在网络中断时已发出的请求如何避免重复扣费(前端幂等 ID)

聊天应用在网络中断时已发出的请求如何避免重复扣费?前端如何用幂等 ID?

  • 网络中断与重试的重复请求
  • 幂等 ID 机制
  • 前端与服务端的配合

网络中断后客户端重试可能让同一请求被服务端处理多次而导致重复扣费。前端在发起请求时生成幂等 ID(uuid)随请求发送,服务端记录已处理过的幂等 ID,重复请求直接返回首次结果而不重复计费/执行。前端重试时复用同一幂等 ID,而非生成新 ID。同时请求带 requestId 关联消息,服务端做去重。幂等 ID 需前后端共同约定,服务端有 24h 等窗口保存。

重试本身是必要的,但必须幂等。幂等 ID 让服务端识别"这是同一请求的重试",从而只计费一次。前端负责生成并复用 ID,服务端负责去重,是防重复扣费的关键。

#
★★

11. 跨标签页打开同一会话时,BroadcastChannel 如何同步流事件、避免重复请求

跨标签页打开同一会话时,BroadcastChannel 如何同步流事件、避免重复请求?

  • BroadcastChannel 的广播机制
  • 流事件同步
  • 避免重复请求(leader 选举)

跨标签页打开同一会话时,用 BroadcastChannel 广播流事件(token、状态、版本),让其他标签页同步更新 UI。避免重复请求:用 leader election(如基于 BroadcastChannel 的 leader 选举算法)让同一会话只由一个标签页持有对 Provider 的活跃请求,其他标签页订阅该流;或注入"正在请求"的接力机制,新标签页检测到已有活跃流则不再发起请求,只同步状态。请求完成后广播最终结果供各标签页去重合并。

多标签页若各自请求会重复扣费且状态混乱。BroadcastChannel 负责同步与协调,leader 选举保证单一请求者。这是"多标签页一致性"的常见解法。

#
★★

12. 跨标签页并发编辑同一会话时,BroadcastChannel 与服务端版本号如何解决冲突

跨标签页并发编辑同一会话时,BroadcastChannel 与服务端版本号如何解决冲突?

  • 并发编辑的冲突来源
  • BroadcastChannel 的实时同步
  • 服务端版本号/乐观锁解决冲突

跨标签页并发编辑同一会话时,BroadcastChannel 在标签页间实时广播编辑事件,让各标签页即时看到对方改动,减少冲突窗口。但最终一致性以服务端为准:每条消息带版本号,提交时做乐观并发控制(提交携带读取时的版本号,服务端校验,不匹配则拒绝并返回最新版本),前端收到冲突提示后合并或让用户选择。BroadcastChannel 负责"即时同步",版本号负责"最终一致性"。

BroadcastChannel 解决"实时可见",版本号解决"并发写冲突"。两者结合:本地协同即时,服务端仲裁权威。并发写必须用版本号/乐观锁,避免 last-write-wins 静默丢数据。

#
★★

13. 前端遥测怎样记录性能和错误而不采集完整 Prompt、文件内容或敏感 URL

前端遥测怎样记录性能和错误而不采集完整 Prompt、文件内容或敏感 URL?

  • 遥测脱敏与最小化采集
  • 性能指标与错误采样
  • 敏感数据避免上传

前端遥测遵循数据最小化:性能指标(首 token 延迟、TTI、FCP、渲染时长)只上报聚合值,不上报内容;错误日志只上报错误类型、堆栈(脱敏)、requestId、消息 id,不包含完整 Prompt 和文件内容;URL 记录时去掉查询参数与敏感片段。对需要诊断的内容,用哈希或脱敏后的占位符(如 prompt_hash)代替原文。在采集层做白名单过滤,只采集允许的字段,并设置采样率。

Prompt 与文件内容是敏感数据,遥测必须脱敏。做法是"只采聚合指标 + 脱敏标识符 + 内容哈希",上报前过滤。这既满足可观测性又守住隐私边界。

#
★★

14. 大型附件上传(>10MB)应如何分片、断点续传、并行上传,前端 UX 如何设计

大型附件上传(>10MB)应如何分片、断点续传、并行上传,前端 UX 如何设计?

  • 分片大小与并行上传
  • 断点续传与进度
  • 前端 UX(进度、取消、重试)

10MB 附件分片(如 8MB)并行上传(限制并发数如 3-4),每片带序号与 Hash,服务端校验后支持断点续传:重连后查询已上传分片,只传缺失部分。前端 UX:显示整体进度条(按已确认分片计算)、取消/暂停、失败分片自动重试与重试按钮、上传中附件可编辑名称。失败时保留已上传分片,重试不重复上传。用 Web Worker 计算 Hash 避免阻塞。

大文件必须分片 + 并行 + 断点续传,否则易超时失败。UX 要透明(进度、取消、重试)且可恢复。Hash 计算放 Worker 保 UI 流畅。

#
★★

15. Web Worker 在 AI 前端应用中适合卸载哪些工作(Markdown 解析、JSON Schema 校验、向量计算)

Web Worker 在 AI 前端应用中适合卸载哪些工作(如 Markdown 解析、JSON Schema 校验、向量计算)?

  • Web Worker 的适用场景
  • 计算密集 vs 渲染任务
  • 数据的传输与隔离

Web Worker 适合卸载不依赖 DOM、计算密集的任务:Markdown 解析与渲染预处理、JSON Schema 校验、向量/嵌入计算、大文件 Hash、结构化输出解析、工具结果序列化等。这些任务放在主线程会阻塞渲染与交互。Worker 不能访问 DOM,因此渲染仍需主线程;适合"数据计算 → 传输结果给主线程渲染"的模式。对超大计算可用 OffscreenCanvas 做离屏渲染。

Web Worker 的关键是"计算与渲染分离"。AI 前端的解析、校验、向量计算都是 CPU 密集且可离线,放 Worker 提升流畅度。要评估传输成本,避免小而频繁的任务因通信开销反而变慢。

#
★★

16. 会话历史的本地缓存与远程同步策略(last-write-wins、CRDT、版本号)

会话历史的本地缓存与远程同步策略(last-write-wins、CRDT、版本号)应如何选择?

  • 各同步策略的适用场景
  • 复杂度与一致性权衡
  • 选择依据

同步策略选择取决于冲突频率与一致性需求:last-write-wins 最简单,适合冲突少、可接受丢更新的场景(如单用户多设备追加消息);版本号 + 乐观锁适合需要检测冲突、可引导用户解决的中等场景(如并发编辑);CRDT 提供无冲突收敛,适合多端实时协同、需要自动合并的场景(如协同编辑),但实现复杂、存储开销大。多数 AI 聊天会话用 last-write-wins + 版本号即可,CRDT 仅在真协同编辑时引入。

没有"万能"策略,要权衡复杂度与一致性。单用户聊天冲突少,last-write-wins 足够;协同编辑才是 CRDT 的主场。版本号是中间地带,提供冲突检测又不引入 CRDT 复杂度。

#
★★

17. 流式输出中的“粘贴文本”与“模型输出”应如何在视觉上区分(背景色、边框)

流式输出中的"粘贴文本"与"模型输出"应如何在视觉上区分(背景色、边框)?

  • 两种来源的视觉区分
  • 视觉一致性(背景色、边框、标签)
  • 无障碍与可辨识度

"粘贴文本"(用户粘贴的原始内容)与"模型输出"用视觉元素区分:模型输出用正常的对话气泡(特定背景色、无边框或细边框),粘贴文本用不同背景色 + 边框 + 标签(如"原文"、"来自粘贴"),或使用虚线边框与不同字重。两种来源用一致的 Design Token 区分,避免混淆。同时保证对比度满足无障碍,并可用文字标签辅助屏幕阅读器。

用户需要区分"这是模型生成的"还是"这是我粘贴的原文",否则容易混淆来源与可信度。视觉区分 + 标签 + 无障碍标注保证可辨识。

#
★★

18. 前端缓存 Provider 列表、模型元信息的能力如何减少首屏延迟,又避免过期数据

前端缓存 Provider 列表、模型元信息的能力如何减少首屏延迟,又避免过期数据?

  • 元信息缓存的收益(减少首屏延迟)
  • 缓存失效策略(版本、TTL、ETag)
  • 过期与重建

前端缓存 Provider 列表、模型元信息(能力、上下文窗口、价格)可减少首屏请求,立即渲染设置面板。用缓存版本号 + 最大年龄(TTL)+ ETag/条件请求:缓存命中直接渲染,后台用 ETag 校验,变更则更新。对易变数据(价格、可用性)设短 TTL,对稳定数据(能力描述)设长 TTL。缓存用版本号使升级该 schema 时整体失效。

元信息缓存的关键是"尽快渲染 + 及时更新"。TTL + 版本号 + 条件请求兼顾减延迟与不过期。Google 的 SWR(stale-while-revalidate)策略也适用:先用缓存,后台刷新。

#
★★

19. 错误重试(断线重连、Provider 错误)应做几次指数退避,避免前端“卡死”等待

错误重试(断线重连、Provider 错误)应做几次指数退避,避免前端"卡死"等待?

  • 指数退避算法
  • 最大重试次数与上限
  • 避免无限等待与卡死

错误重试采用指数退避:初始间隔(如 1s)每失败翻倍,加随机抖动(jitter)防止惊群,最多重试 3-5 次后停止,并设最大间隔上限(如 30s)。重试前区分错误类型:可重试(网络抖动、5xx、限流)与不可重试(4xx、认证失败、参数错误)。每次重试都应有超时,避免无限等待导致的前端"卡死"。重试时给用户显示状态,保留停止/跳过入口。

无限重试会卡死 UI 且放大负载。指数退避 + 上限 + 抖动 + 错误分类是标准做法。关键是不能无限重试,且不可重试错误直接反馈用户。

#
★★

20. 聊天应用切换到后台标签页时,是否应暂停流渲染(性能优化)

聊天应用切换到后台标签页时,是否应暂停流渲染(性能优化)?

  • 后台标签页的渲染策略
  • 暂停/恢复流渲染
  • 与请求、状态的一致

后台标签页应暂停主动的流渲染(DOM 更新、动画),以节省主线程与资源。但不应暂停数据接收与请求本身:流仍在后台接收,token 累积到缓冲区,回到前台时一次性渲染最新状态。若长时间后台,可暂停/降频请求并通过 WebSocket 或 heartbeat 保持连接,必要时通知服务端暂停生成。恢复前台时用最新状态渲染并同步滚动。用 Page Visibility API 检测前后台。

后台渲染无意义且浪费资源,应暂停渲染但保留数据接收与连接。恢复时快速渲染最新状态。这是性能优化与用户感知的平衡。

#
★★

21. 聊天前端如何用消息 ID、游标和幂等提交处理 SSE 重连,避免重复 Token 或消息顺序错乱

聊天前端如何用消息 ID、游标和幂等提交处理 SSE 重连,避免重复 Token 或消息顺序错乱?

  • SSE 重连的事件连续性
  • 消息 ID 与游标去重
  • 幂等提交与顺序保证

SSE 重连时,服务端用事件流中的递增事件 ID(Last-Event-ID 头)与游标让客户端从断点续传;前端用消息 ID 和事件 ID 去重,重复 token 直接丢弃。幂等提交:客户端提交每条消息带消息 ID + 幂等键,服务端去重,避免网络重发导致重复消息。顺序保证用单调递增序列号,客户端只接受比当前大的序列号,乱序/旧事件晚到丢弃。写入时校验 requestId 隔离不同流。

SSE 重连要同时解决"续传"(游标 + Last-Event-ID)、"去重"(事件 ID + 幂等键)、"顺序"(序列号校验)。三者配合才能避免重复 token 与乱序。

#
★★

22. React 或 Vue 的增量渲染怎样避免每个 Token 触发全树重渲染,并保持 Markdown 和代码块稳定

React 或 Vue 的增量渲染怎样避免每个 Token 触发全树重渲染,并保持 Markdown 和代码块稳定?

  • 框架的增量渲染机制(memo、key、局部更新)
  • 避免每 token 全树重渲染
  • Markdown/代码块的稳定性

流式更新时避免每次 token 触发全树重渲染:把消息列表拆成独立组件,用 memo/React.memo 或 Vue 的 shallow 比较,只更新变化的叶子节点;流式内容用并行的缓冲区批量更新(rAF 合并),而非每 token setState。Markdown 解析结果用缓存(按内容 hash),未变化部分复用 DOM;代码块用稳定 key 和独立组件,避免重渲染导致高亮/滚动丢失。用只更新文本节点的 diff 策略。

框架的 diff 加上 memo 能减少重渲染范围,但流式高频更新仍需"批量合并"策略。关键是把"数据流"与"渲染"解耦,用 rAF 节流 + 局部 memo,保持 Markdown 和代码块 DOM 稳定。

#

23. 用户在工具调用期间刷新页面时,前端如何从服务端恢复 Agent 状态并展示可继续或需审批的节点

用户在工具调用期间刷新页面时,前端如何从服务端恢复 Agent 状态并展示可继续或需审批的节点?

  • Agent 状态的服务端持久化
  • 刷新后的状态恢复
  • 可继续/需审批节点的展示

服务端应在工具调用期间持久化 Agent 状态(当前步骤、已完成的工具调用、待审批节点、上下文),前端刷新后通过会话 ID 拉取状态,恢复到对应节点展示。若 Agent 处于 awaiting-tool 且工具已执行,前端展示完成结果并让用户继续;若处于 awaiting-confirmation(需审批),前端展示确认卡让用户决定继续或中止。恢复后重新订阅流(用 requestId 续接或重新发起)。前端渲染"可继续/需审批"的状态卡,指引下一步操作。

刷新不能丢失 Agent 执行进度,服务端必须持久化状态。前端恢复后按状态机渲染"继续/审批"节点,让用户接管。这是长时 Agent 任务的恢复能力。

#

24. 如何设计乐观 UI、取消生成和重新生成操作,使客户端状态与 Provider 已产生的计费事实一致

如何设计乐观 UI、取消生成和重新生成操作,使客户端状态与 Provider 已产生的计费事实一致?

  • 客户端状态与计费事实的差异
  • 取消/重新生成的计费语义
  • 状态与计费记录的同步

客户端乐观状态与 Provider 计费事实可能不一致(如 UI 显示已停止但服务端实际已生成到某处)。设计上:取消与重新生成都携带 requestId,服务端记录实际消耗的 token/计费,前端从服务端拉取"实际产生"的计费事实(token 数、已生成内容)来校正 UI,而非仅凭乐观假设。计费事实以服务端审计为准,前端展示的 token 用量最终以服务端回执为准。重新生成会真正产生新请求,需向用户确认以避免重复扣费。

乐观 UI 是无害的"预期",但计费必须基于事实。用 requestId 关联服务端记录,前端结束时以服务端结算为准同步 UI,避免"UI 说没生成但账单已扣费"的偏差。

#

25. 长对话中的虚拟列表、上下文摘要和滚动锚点如何协作,避免恢复历史时跳动或丢失未读内容

长对话中的虚拟列表、上下文摘要和滚动锚点如何协作,避免恢复历史时跳动或丢失未读内容?

  • 虚拟列表与滚动锚点的协作
  • 上下文摘要与加载
  • 未读内容的保持

长对话恢复时:虚拟列表只渲染可视区,滚动锚点记录用户当前阅读位置(消息 id + 偏移),恢复后锚定到该位置避免跳动。上下文摘要(超长对话的早期摘要)作为虚拟列表中的"占位节点",点击可展开原始内容。未读内容:恢复时记录"上次已读位置",新消息滚到底部提示未读数,避免自动滚动丢失用户正读的内容。锚点用稳定 id 而非像素偏移,防止高度变化导致漂移。

三者协作:虚拟列表控制 DOM 规模,锚点稳定位置,摘要减少要加载的数据。核心是"恢复时锚定原位置 + 不自动抢走未读内容"。

#

26. 流式结构化 JSON 尚未闭合时,前端如何显示可用字段并在解析失败后平滑回退到文本展示

流式结构化 JSON 尚未闭合时,前端如何显示可用字段并在解析失败后平滑回退到文本展示?

  • 部分 JSON 的增量解析
  • 可用字段的展示
  • 解析失败的平滑回退

流式结构化 JSON 未闭合时,用增量解析器(如 partial-json 解析器)解析已累积的片段,提取已完整可用的字段先行渲染,未闭合部分显示加载占位。当最终 JSON 无效(被截断或模型输出非 JSON)时,平滑回退到文本展示:捕获解析错误,把原始文本按 Markdown 渲染,并提示"结构化数据未生成,已展示为文本"。避免抛出未捕获异常导致页面崩溃。回退时可保留已解析出的部分可用字段。

流式 JSON 天生可能未闭合,增量解析让已有字段尽早可见。解析失败要优雅降级为文本,不能崩溃。乐观解析 + 熔断回退是关键。

#

27. 如何用 Web Vitals、首 Token 延迟和用户中断率评估 AI 界面性能,而不是只测接口 P95

如何用 Web Vitals、首 Token 延迟和用户中断率评估 AI 界面性能,而不是只测接口 P95?

  • 用户视角的核心指标(LCP、INP、首 token 延迟)
  • 业务指标(中断率、完成率)
  • 与接口 P95 的差异

评估 AI 界面性能应采用用户视角指标:Web Vitals(LCP、INP、CLS)衡量渲染与交互;首 Token 延迟(TTFT)衡量流式开始响应的速度——这比接口 P95 更能反映用户感知;用户中断率(主动停止生成的比例)衡量流式体验与可控性;还有完成率、重新生成率、内容稳定性。接口 P95 只反映服务端单接口,忽略了渲染、流式首包、用户等待与是否被打断。综合这些指标才能反映真实体验。

P95 是单一接口服务端延迟,而 AI 体验是"首包 + 流式 + 交互 + 用户可控"的综合。首 Token 延迟、中断率、Web Vitals 才是用户可感知的指标,评估应覆盖这些。