腾讯pcg前端日常一面整理笔记(三十二)
1. 介绍一下项目中的虚拟列表功能
答案
项目里的虚拟列表功能,核心是“数据很多,但页面上只渲染可视区附近的节点”。
- 根据滚动位置算出当前应该显示的数据范围。
- 只渲染这部分数据,其他部分用占位高度撑开滚动条。
- 这样可以显著减少 DOM 数量,提升首屏和滚动性能。
- 如果是动态高度列表,还要维护高度缓存和偏移量。
2. 为什么要使用虚拟列表
答案
使用虚拟列表的核心原因是:数据量大时,直接渲染全部 DOM 成本太高。
- 节点过多会导致首次渲染慢。
- 滚动时布局和绘制压力也会变大。
- 虚拟列表通过限制真实渲染节点数量,显著降低性能压力。
3. 了解过 Vue 开源虚拟滚动组件吗
答案
可以回答“了解过,而且核心思想和自己实现的思路是一致的”。
- 常见开源方案都是按可视区裁剪渲染。
- 不定高场景一般会维护高度缓存和偏移表。
- 面试里重点不是背组件名,而是讲清核心原理和取舍。
4. 窗口缩放导致高度变化,高度缓存表如何处理
答案
如果列表项高度会受容器宽度变化影响,窗口缩放后原来的高度缓存可能失效。
- 需要在 resize 后重新测量受影响项。
- 更新高度缓存表和偏移量。
- 为了避免频繁重算,一般会做节流或只重算可视区附近项。
5. 用什么 API 和事件测量元素高度
答案
常见方案有两类:
- 直接测量:
getBoundingClientRect、offsetHeight。 - 监听变化:
ResizeObserver。
如果是响应式场景,ResizeObserver 往往更适合持续追踪高度变化。
6. 移动端小游戏适配到桌面端正常游玩
答案
这种适配题可以从输入、布局和性能三方面回答。
- 输入层要适配鼠标和键盘,不只支持触摸。
- 布局层要考虑宽高比、横竖屏和桌面分辨率。
- 还要处理素材尺寸、事件映射和性能差异。
7. 什么是事件冒泡和事件捕获
答案
事件传播分为捕获阶段、目标阶段和冒泡阶段。
- 捕获阶段从外层祖先往目标元素走。
- 冒泡阶段从目标元素再往外层祖先回传。
- 默认大多数监听注册在冒泡阶段。
8. 数组和链表的区别
答案
数组和链表的区别主要在访问方式和增删成本。
- 数组内存通常更连续,随机访问快。
- 链表插入删除局部节点更方便,但随机访问慢。
- JavaScript 里的数组在引擎层面不是简单“纯链表”或“纯原生数组”,而是做了很多优化的动态结构。
9. JS 数组底层是数组还是链表
答案
JavaScript 引擎里的数组不是简单的“纯链表”或“纯原生数组”,而是做了很多优化的数据结构。
- 在元素连续、类型稳定时,会尽量按高效数组方式处理。
- 如果出现稀疏索引、频繁删除或类型混乱,内部优化策略会退化。
10. 浏览器计时器适合用什么数据结构
答案
浏览器计时器通常会用优先队列或最小堆一类结构管理。
- 这样可以快速拿到最近一个应该触发的定时器。
- 插入新定时器和弹出最早触发任务的效率都比较高。
11. 最小堆是什么、底层如何实现
答案
最小堆是一种完全二叉树结构,要求父节点值不大于子节点值。
- 堆顶始终是最小值。
- 插入时通过上浮调整,删除堆顶后通过下沉调整。
- 常用于优先队列、定时器调度、TopK 等场景。
12. requestAnimationFrame 是什么、作用
答案
requestAnimationFrame 是浏览器提供的按帧调度 API。
- 它会在下一次重绘前执行回调。
- 适合动画、滚动同步、增量渲染这类和绘制节奏强相关的场景。
- 比纯
setTimeout更贴近浏览器渲染时机。
13. 大模型输出快慢对 RAF 的优劣势
答案
如果大模型输出特别快,逐 token 刷新可能导致频繁重渲染。
- 这时
requestAnimationFrame可以把多次更新合并到一帧里,减少抖动。 - 如果模型输出很慢,RAF 的优势就没那么明显,简单批量更新也够用。
- 本质是用 RAF 把 UI 更新节奏和浏览器绘制节奏对齐。
14. 如何平衡 RAF 在不同输出频率下的表现
答案
可以做“缓冲 + 条件刷新”策略。
- 高频输出时用 RAF 合并一帧内的多次更新。
- 低频输出时可以直接更新,减少等待。
- 本质是根据输出频率动态切换刷新策略。
15. Vue nextTick 是什么、作用
答案
nextTick 的作用是等 Vue 完成一次 DOM 更新后,再执行回调。
- 因为 Vue 的响应式更新通常是异步批量执行的。
- 所以如果你修改了状态后立刻读 DOM,拿到的可能还是旧值。
- 这时用
nextTick可以确保拿到更新后的 DOM。
16. 如何结合 nextTick 优化流式渲染
答案
流式渲染通常是“服务端持续返回分片,前端边收边渲染”。
- 服务端按 chunk 或事件流持续输出内容。
- 前端通过
EventSource、fetch + ReadableStream或封装 SDK 持续读取数据。 - 每拿到一段内容,就追加到消息状态里,再触发局部更新。
- 为了避免频繁重渲染,通常会做分帧刷新、批量合并更新或按 token 缓冲后再刷。
17. 如何解决 Markdown 渲染抖动
答案
Markdown 流式渲染抖动,通常是因为每次只来一点数据就整段重新解析和重渲染。
- 可以对 token 做缓冲,按帧或按块刷新,而不是每个字符都重算。
- 对于已经稳定的内容尽量复用,只更新尾部增量部分。
- 复杂内容可以拆成块级组件,减少整棵树反复重建。
18. 增量渲染能否解决抖动
答案
Markdown 流式渲染抖动,通常是因为每次只来一点数据就整段重新解析和重渲染。
- 可以对 token 做缓冲,按帧或按块刷新,而不是每个字符都重算。
- 对于已经稳定的内容尽量复用,只更新尾部增量部分。
- 复杂内容可以拆成块级组件,减少整棵树反复重建。
19. JS 垃圾回收机制
答案
JavaScript 垃圾回收的核心是判断对象是否仍然可达。
- 主流引擎以标记清除为基础。
- 现代实现通常还会加分代回收、增量回收、并行回收。
- 业务里更重要的是主动清理定时器、事件监听和不再需要的大对象引用。