真实面经题目 · 原创解析

懒加载和虚拟列表如何优化首屏性能?

懒加载通过延后非首屏图像、组件、路由和数据请求,减少首屏需要下载和执行的资源;虚拟列表通过只渲染视口附近的列表项,减少 DOM 数量、布局计算和绘制成本。两者都能优化首屏,但前提是不要延后首屏核心内容,并要处理占位尺寸、预加载、滚动体验和可访问性。

出现于:字节跳动 · 前端

60 秒回答模板

懒加载解决的是首屏资源过多的问题:非首屏图像、视频、组件、路由代码、低优先级数据可以等用户接近时再加载,常用 loading=lazy、IntersectionObserver、动态 import、路由级 code splitting。虚拟列表解决的是一次性渲染节点过多的问题:长列表只保留视口范围和少量 overscan 元素,滚动时复用或替换节点,用总高度占位维持滚动条长度。优化首屏时,首屏核心图像、标题、关键 CSS 和必要数据不能懒加载;长列表首屏只渲染可见项,可以显著减少 DOM、样式计算、布局和水合成本。

考点 减少资源
难度 真实面经高频题
回答目标 讲清机制、边界和追问

深入解析

01

懒加载目标

懒加载的核心是把非关键工作移出首屏关键路径。首屏只需要用户马上能看到和使用的资源,首屏之外的图像、视频、图表、评论、推荐模块、低频弹窗和次级路由可以延后。这样网络层减少初始请求和字节数,主线程减少解析执行,浏览器也少做样式、布局和绘制工作。

02

懒加载实现

图像资源可以使用原生 loading=lazy,也可以用 IntersectionObserver 在元素接近视口时替换真实 src;组件和路由可以用动态 import 按需加载;数据可以在模块进入视口前预取。实际工程中还要设置 width、height 或 aspect-ratio,避免资源加载后撑开布局;对即将进入视口的内容提前加载,避免用户滚动到位置时仍是空白。

03

虚拟列表原理

虚拟列表针对长列表首屏和滚动性能。假设有一万条数据,页面不需要同时创建一万个 DOM 节点,只需要渲染当前视口能看到的几十条和前后缓冲项。容器用总高度模拟完整滚动范围,内部列表通过 translate 或定位移动到对应位置。这样 DOM 数量稳定,布局和绘制压力与总数据量解耦。

04

首屏收益

在 SSR 或水合场景中,虚拟列表还可以减少首屏 HTML 大小和 hydration 节点数量;在纯客户端渲染中,它减少初始 createElement、样式计算和布局时间。懒加载减少资源和代码,虚拟列表减少节点和渲染工作,两者组合适合信息流、搜索结果、消息列表、日志列表、商品列表等首屏下方数据很多的页面。

05

边界风险

懒加载不能用于首屏 LCP 资源,否则会降低核心内容优先级;虚拟列表不适合节点很少的列表,也会带来动态高度测量、滚动定位、键盘导航、查找、打印、浏览器页内搜索和可访问性问题。工程上要根据列表规模、单项高度稳定性、SEO 需求和交互复杂度判断是否值得引入。

易错点

  • 把首屏核心图像和主要内容也懒加载,导致 LCP 变慢。
  • 认为虚拟列表只减少 DOM 数量,没有说明它也减少布局、绘制、水合和主线程执行成本。
  • 给少量列表也引入复杂虚拟列表,增加维护成本却没有明显性能收益。
  • 忽略占位尺寸和 overscan,导致懒加载布局偏移或虚拟列表滚动白屏。

面试官追问

懒加载为什么可能让 LCP 变差?

如果首屏最大内容元素被懒加载,浏览器会降低它的发现和加载优先级,导致核心内容更晚出现。首屏关键图像应直接加载,并可通过 preload、fetchpriority、尺寸压缩等方式优化。

虚拟列表如何保持滚动条长度正确?

它会用总数据量乘以行高,或用动态测量后的累计高度创建一个占位空间。实际渲染的可见项再通过偏移定位到当前滚动位置,因此滚动条看起来对应完整列表。

动态高度列表为什么更难虚拟化?

固定高度可以直接用索引计算偏移;动态高度需要测量每项真实高度并维护累计高度,滚动跳转、快速滚动和数据变化时都要校正偏移,否则容易出现抖动或位置不准。

懒加载和代码分割是什么关系?

代码分割把 bundle 拆成多个块,懒加载决定某些块何时加载。路由级或组件级动态 import 可以让首屏只下载必要代码,用户进入对应功能时再加载剩余代码。