题干与适用场景
这道前端题考察长列表的交互状态、网络边界和可访问性。重点不是把观察器接到一个哨兵元素,而是让加载、取消、失败、重试、恢复位置和替代导航形成完整契约。
面试官考察什么
- 能否用 Intersection Observer 或等价机制触发加载,并控制预取距离。
- 能否通过游标、请求去重和顺序保护避免重复页、乱序页和无限重试。
- 能否让键盘、读屏、缩放和低速网络用户获得可见且可操作的状态。
- 能否在无限滚动不适合时提供分页、跳到主要内容和位置恢复。
回答前需要澄清的问题
先确认列表是时间线、搜索结果还是网格,排序是否稳定,服务端能否提供游标,单页大小和目标首屏延迟是多少。还要确认是否要求分享某个位置、是否支持返回后恢复滚动、内容高度是否变化,以及是否存在移动端流量限制。
30 秒回答框架
我会用稳定游标分页和一个可移除的加载哨兵。只有没有进行中的请求、还有下一页且哨兵进入预取区时才加载;响应按游标去重并保持顺序。加载、失败、重试和已到底部都显示为可聚焦的状态。列表提供明确的分页或“加载更多”替代入口、跳到主要内容和返回位置恢复,避免把滚动作为唯一导航。
分步骤深入解答
1. 定义数据与位置契约
优先使用不受插入数据影响的游标,响应携带下一游标和稳定的条目 id。客户端维护已知游标集合、条目 id 集合和当前请求状态,避免用页码在前方插入时造成重复或漏项。返回页面时记录路由、游标和滚动偏移,并在数据重新可用后恢复到可见条目。
2. 设计触发和并发控制
Intersection Observer 只负责发出“接近底部”的信号,不直接启动无界请求。触发前检查 hasNext、loading、重试预算和网络状态;同一游标只允许一个请求。预取阈值应根据内容高度和网络延迟调节,快速滚动时仍保留显式按钮,避免观察器连续触发。
3. 处理响应、取消和失败
响应必须校验请求游标仍是当前预期,旧请求晚到时丢弃或合并到已知集合。用户离开页面可取消未完成请求,但取消不应清除已经展示的数据。失败状态显示原因、重试按钮和已加载数量;指数退避只用于自动重试,并设置上限,避免服务端故障时形成请求风暴。
4. 建立可访问的替代路径
哨兵本身不应成为唯一控制点。提供可聚焦的“加载更多”、明确的加载中和已到底部提示,使用语义列表、稳定的标题层级和足够的焦点可见性。长列表可提供分页、跳过重复内容和“回到列表顶部”链接;焦点移动应有明确规则,不能因为新节点插入而把键盘用户送到页面末尾。
5. 验证性能、可用性和恢复
测试键盘只用 Tab 和 Enter 完成多页加载,检查读屏能感知新增内容与失败。用慢网、离线、快速滚动、重复点击、返回前进、缩放 200% 和动态图片高度测试。观察请求数、重复率、首屏与每页延迟、取消率和恢复成功率;长列表必要时采用虚拟化,并验证虚拟化不会破坏语义和焦点。
高质量示范回答
我会把列表建立在稳定游标和条目 id 之上,客户端记录已消费游标、已见 id、进行中的请求和重试次数。哨兵进入预取区只发出一次加载信号,响应必须匹配当前游标,重复或乱序数据被去重。加载失败显示可聚焦的重试按钮,离线时保留已加载内容。无限滚动旁边提供“加载更多”、分页或跳转入口,保证键盘和读屏用户不依赖滚轮;返回页面时按条目 id 恢复位置。最后用慢网、离线、快速滚动、缩放、返回前进和屏幕阅读器验证。
常见错误
- 直接用页码拼接数据,忽略前方插入导致的重复和遗漏。
- 每次观察器回调都发请求,没有游标去重和进行中锁。
- 失败后无限重试,或把重试按钮做成不可聚焦的提示文字。
- 把滚动作为唯一导航,用户无法用键盘加载、分页或回到顶部。
- 新内容插入后焦点跳到末尾,读屏用户失去当前位置。
- 只测快速桌面网络,不测离线、缩放、动态高度和返回恢复。
追问及应对
为什么不用页码分页?
当数据会新增或删除时,页码边界会移动。稳定排序加游标能减少重复和漏项;若业务允许快照,也可以用快照 token 明确同一次浏览的边界。
哨兵触发太早会浪费请求,怎么调?
根据条目平均高度、滚动速度、网络延迟和取消率调节 root margin,并把它作为可观测参数。预取仍应受 hasNext、并发锁和预算约束。
新数据插入列表顶部时如何保持阅读位置?
先记录锚点条目及其相对偏移,再插入新节点并补偿滚动偏移;对读屏和键盘用户提供“有新内容”按钮,由用户决定何时跳转。
什么时候应该改成分页?
需要深链接、精确跳页、搜索结果可比较、列表很长或内容消费有明确段落时,分页通常更可预测。可以保留渐进加载,但必须提供可见的分页或加载更多入口。