苏州小厂 React 前端面试:项目拷打、虚拟列表与 React 18 新特性
- base
- 苏州
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 简历上的项目有什么难点?你是怎么解决的、怎么实现的?
- 你项目中使用了虚拟列表,它的原理是什么?
- 你写了性能优化,那你查看 performance 时具体看哪三项指标?
- 你这个虚拟列表是使用第三方库还是自己手写?
- 虚拟列表如果某一个元素高度发生变化,会发生什么?你是怎么处理的?
- 闭包是什么?
- 原型链是什么?
- React 18 有什么新特性?
- 对 Vue 有了解吗?
- 你是怎么设计数据结构的?
- 你这里的数据流是怎么处理的?
《参考解析》
项目拷打:考的不是功能清单,是你能不能把一件事讲完整。 「项目有什么难点、怎么解决、怎么实现」是前端面试最常见的开场,面试官想听的是背景、你负责的部分、卡点、备选方案对比、最终取舍和可量化的结果,缺哪一段就会被追着补哪一段。稳妥的讲法是一场只挑一个项目、一个难点讲深:先说清约束(数据量多大、要兼容什么、原来的做法哪里不够),再给两三个候选方案并说明为什么选这个、代价是什么,最后落到具体实现与指标变化(首屏时间、包体积、长列表滚动帧率)。最忌讳的是把需求文档复述一遍、或者说「用了某某库就解决了」——面试官下一句一定是「为什么用它、你自己做了哪部分」。项目里提到虚拟列表、性能优化这类词,等于主动把后面的题递给了面试官,凡是在简历上写的关键词都要能往下讲两层。
虚拟列表:原理、库与手写、以及动态高度这个真考点。 核心思路是只渲染可视区域加一小段缓冲的条目,DOM 数量从「数据条数」降到「一屏条数」,滚动时按 scrollTop 算出起始索引和偏移量,用一个撑满总高度的占位容器保证滚动条长度正确,渲染出来的条目用绝对定位或 transform 移到对应位置,滚动事件做节流或用 requestAnimationFrame 合并。用第三方库还是手写,取决于需求有多复杂:react-window / react-virtualized / @tanstack/react-virtual 已经把滚动边界、横向列表、粘性分组、无障碍处理好了,项目排期紧就用库;手写适合行高固定、结构简单的场景,代码量和包体积更小,但要自己兜住滚动抖动、白屏和浏览器滚动锚定。真正容易翻车的是行高不固定:「单项高度变化」会直接打破「第 n 项偏移 = n × 行高」这个前提,表现为内容错位、滚动条跳动或中间出现空白。通行解法是让每一项自测高度——ResizeObserver 或库提供的测量回调拿到真实尺寸后写进位置缓存,偏移量改用前缀和 + 二分查找定位,已经滚过去的内容高度变化时还要按差值补偿 scrollTop,避免视觉上「跳一下」;做不到精确测量时,退而求其次给一个估算高度、逐步回填,并接受滚动过程中的轻微修正。
性能指标与前端八股,答结论也要答口径。 「performance 三项指标」有歧义,先确认面试官指的是哪一套再答:如果是 Core Web Vitals,三项是 LCP(最大内容绘制,衡量加载体感)、INP(交互到下次绘制,衡量响应性,2024 年起取代 FID)、CLS(累计布局偏移,衡量视觉稳定性);如果对方说的是「首屏三项」,则多指 FP / FCP / LCP 这一组加载时间点。答的时候补一句怎么测量:PerformanceObserver 监听 largest-contentful-paint、layout-shift、event 这些条目类型,或本地用 Lighthouse、线上用真实用户监控(RUM)按分位数看,并说清优化手段的对应关系——LCP 主要靠资源体积、关键渲染路径和 CDN,CLS 靠给图片和占位元素预留尺寸、别在已有内容上方插入元素。闭包是函数连同它定义时所在的词法环境的组合,所以内层函数能在外层函数返回之后继续读到那些变量;它支撑了模块私有变量、柯里化、防抖节流,代价是变量被引用就不释放,长生命周期里容易内存增长,React 里「回调读到旧 state」的 stale closure 也是它的另一面。原型链是属性查找机制:对象自身找不到就沿 [[Prototype]] 往上找,直到 Object.prototype 再到 null,instanceof、class extends、寄生组合继承都建立在这条链上。React 18 的关键词是并发特性:createRoot 新根 API、自动批处理(连 Promise、setTimeout 里的更新也合并)、startTransition / useTransition 标记非紧急更新、useDeferredValue、useId、useSyncExternalStore,以及 Suspense 配合流式 SSR。被问到 Vue 时照实说了解和用到什么程度就行,能对比「模板编译期优化、Proxy 响应式」与 React「运行时 diff、手动 memo」的差异是加分项,不懂装懂最容易被追问穿。
数据结构与数据流设计:先问场景,再谈取舍。 这两问都是开放题,答法要先把场景问清楚——列表是只读展示还是要频繁增删改?数据是服务端分页还是一次性全量?典型回答是用「以 id 为键的 Map + 维护顺序的 id 数组」代替嵌套数组,把按 id 查找和更新压到 O(1),渲染时再按顺序映射,避免 findIndex 加 splice 的 O(n) 开销;树形数据用 id → node 的扁平表加 parentId,比递归嵌套好做移动和展开。数据流则讲清单向:视图触发 action → 集中式 store 或 reducer 更新状态 → 选择器派生出视图需要的数据 → 视图重渲染,配合不可变更新让比较和回溯有依据。一个容易加分的点是区分「服务端状态」和「UI 状态」:远端数据交给 React Query / SWR 这类缓存层管理(缓存、失效、重试、去重都在里面),全局 store 只放真正跨组件共享的 UI 状态,不要把接口返回的数据再抄一份进去,否则两处状态一定会不同步。作者在结尾说自己「估计又寄了」,也把差距归到项目和八股两头——这类小厂面试通常就是项目拷打加高频八股各占一半,把简历上每个关键词都准备到能往下讲两层,再把这几个高频八股过一遍,性价比最高。