Hello,大家好,我是 Sunday
性能优化一向是前端面试中的常见问题,很多同学面对性能优化问题时都回答的不好。所以,咱们今天就把 前端性能优化问题 进行 全解析。
像什么 页面加载慢、白屏时间长、滚动卡顿、图片加载缓慢...... 的,一篇文章,咱们全部带大家了解下!
一、首屏加载优化
首屏加载是指用户打开页面到第一个可视内容呈现出来之间的时间。它直接影响用户对网站的第一印象,因此在前端性能优化中具有最高的优先级。
优化首屏加载的目标是尽可能快地把关键内容展示给用户,减少白屏时间,提升首屏体验。
常见的优化方式包括资源按需加载、预加载关键资源、使用服务端渲染或静态生成等。
1. 资源按需加载(Lazy Load)
在前端应用中,JS、CSS、图片等资源如果全部打包在一起加载,会极大拖慢页面的加载速度。按需加载的核心思路是:只加载当前页面渲染所必须的资源,其余资源延后加载或在需要时再加载。
-
JavaScript 按需加载
使用构建工具(如 Webpack 或 Vite)时,可以借助动态导入(
import()
)来实现路由级别或组件级别的代码拆分(Code Splitting)。例如,在 Vue 或 React 应用中,每个路由页面或大型模块可以被拆分成独立的 chunk 文件,只有用户进入该页面时才会加载对应的 JS 文件。
-
图片懒加载
页面中不在首屏展示的图片可以使用懒加载技术,延迟到滚动进入视口时再进行加载。可以直接使用
loading="lazy"
属性,无需引入第三方库。但是,如果想要做到更好可控性的懒加载,择校使用
IntersectionObserver
来监听元素是否进入视口,从而决定是否加载图片。 -
第三方模块按需引入
大型库如 Lodash、Moment 等可以按需导入,例如使用
lodash-es
代替整个lodash
。日期处理可以使用dayjs
替代体积庞大的moment
。这类优化通常可以通过 Tree Shaking 和构建工具的配置实现。
2. 预加载关键资源
即使按需加载能够减少首屏资源的体积,但某些资源仍然是**"一定会用且必须尽早使用"**的,例如首屏字体、主视觉图、关键 JS 脚本等。这些资源可以通过预加载机制提前告知浏览器加快加载节奏。
-
preload
<link rel="preload">
用于告诉浏览器某个资源是高优先级资源,应该尽早加载。例如:
html
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin="anonymous">
PS:as
属性指定了资源的类型,有助于浏览器进行更好的优先级调度。
-
preconnect
与dns-prefetch
当页面需要请求第三方资源(如 CDN、第三方接口等)时,可以使用
<link rel="dns-prefetch">
和<link rel="preconnect">
来减少 DNS 查询和 TCP 建连的耗时。前者预解析域名,后者预建立连接。
html
<link rel="preconnect" href="https://example-cdn.com" crossorigin>
<link rel="dns-prefetch" href="https://example-cdn.com">
特别是在页面初始阶段要从多个域名拉取资源时,这项优化能有效减少网络延迟。
3. 服务端渲染(SSR)或静态站点生成(SSG)
现代单页应用(SPA)由于前端 JS 执行完成之后才会渲染页面内容,因此在网络较差或设备性能较弱的情况下容易出现长时间白屏的现象。
为了解决这一问题,可以考虑使用 服务端渲染或静态站点生成 的方式,提升首屏内容呈现速度。
-
服务端渲染(SSR)
SSR 的原理是前端框架(如 Vue、React)在服务器端完成首屏 HTML 的渲染并返回给客户端,浏览器接收到完整的 HTML 后可立即展示页面,之后再加载 JS 接管页面行为。常见框架如 Next.js(React)、Nuxt(Vue)等都支持 SSR。SSR 可显著减少白屏时间,提升 SEO 表现,但会增加服务器负担和架构复杂度。
-
静态站点生成(SSG)
SSG 会在构建阶段生成每个页面的静态 HTML 文件并部署到 CDN。当用户访问页面时,直接从 CDN 读取 HTML,无需等待服务器实时渲染,响应速度极快。适合内容基本固定的展示型网站,如博客、文档站等。
-
混合渲染
现代框架如 Next.js、Nuxt3 支持按页面配置渲染模式,可以为不同路由选择 SSR、SSG 或客户端渲染,提升灵活性。
二、资源压缩与合并
前端页面性能的瓶颈很大一部分来源于资源的传输效率,而资源压缩与合并正是减少资源体积和请求次数的有效手段。
1. HTML / CSS / JS 压缩
前端构建工具默认都会对代码进行压缩处理,包括移除空格、注释、换行符,以及对变量名、函数名进行混淆,从而减少文件体积。不同的资源类型采用不同的压缩工具:
- JS 压缩 :常用工具包括 Terser、UglifyJS。现代构建工具如 Vite 和 Webpack 内部默认集成了 Terser 插件,生产环境自动开启压缩。
- CSS 压缩 :使用
cssnano
、clean-css
等工具压缩 CSS,去除重复规则、空格、注释等。PostCSS 的生态中也有大量 CSS 优化插件。 - HTML 压缩 :可以使用
html-minifier
工具。虽然 HTML 本身体积不大,但在 SSR 或 SSG 输出的静态页面中,压缩可以减少每次请求的内容大小。
2. 图片压缩与格式优化
图片通常是页面体积中占比最大的资源,特别是在内容展示型或电商类网站中,一张未压缩的大图就可能有数 MB,大量图片会严重拖慢页面加载速度。
- 图片压缩工具 :可以在构建流程中引入
imagemin
插件实现自动压缩。也可以使用一些在线压缩工具,比如:Squoosh、TinyPNG、ImageOptim(Mac)等 - 格式优化 :传统格式如 PNG、JPEG 已无法满足现代 Web 的性能要求。可以使用新一代图像格式,比如:
WebP、AVIF
- SVG 图像优化 :对于图标类图片,优先使用 SVG。配合
svgo
工具进行压缩,可显著减小 SVG 文件体积。
3. 减少请求数
浏览器对于同一域名下的并发请求数是有限的(HTTP/1.1 下约为 6 个),如果页面请求过多会被排队执行,影响加载速度。那么此时,就可以通过资源合并可以减少请求数量。
需要注意的是,合并资源的策略在 HTTP/2 中发生了变化 。由于 HTTP/2 支持多路复用,多个请求可并发传输,合并的必要性有所降低,但资源体积越小仍然越好,因为请求数虽不再是瓶颈,传输大小和解析速度依旧重要。
4. 使用 CDN 分发资源
资源压缩之后,部署方式同样影响加载速度。将静态资源托管在 CDN(内容分发网络)上,可以有效提升资源的下载速度,特别是面向全国或全球用户时。
三、缓存机制优化
缓存机制的核心目标是"减少重复资源的网络请求",从而提升页面加载速度,减轻服务器压力。
前端常见的缓存方式主要包括:HTTP 缓存、本地存储缓存、Service Worker 离线缓存等。
1. HTTP 缓存
HTTP 缓存是浏览器最基础、最重要的缓存方式,主要通过响应头信息进行控制。常见的缓存方式分为两种:强缓存和协商缓存。
-
强缓存(强制使用缓存)
通过设置
Cache-Control: max-age=xxx
或Expires
响应头,浏览器在设定时间内不再向服务器发起请求,而是直接使用本地缓存。例如:iniCache-Control: max-age=31536000
当用户在有效期内再次访问页面时,请求不会发送,直接命中本地缓存,速度极快。
-
协商缓存(验证缓存)
设置
ETag
或Last-Modified
响应头,浏览器会在下一次请求时携带If-None-Match
或If-Modified-Since
,服务器比对后决定资源是否更新。如果资源未修改,返回
304 Not Modified
,浏览器继续使用本地缓存;如果已更新,则返回新的资源。
2. 本地存储缓存(localStorage / sessionStorage / IndexedDB)
-
localStorage
永久存储,除非手动清除或用户清空浏览器缓存,适合存储体积小、不会频繁更新的数据,如用户设置、Token、主题配置等。
-
sessionStorage
会话级别的缓存,页面关闭即失效。适用于需要临时缓存的数据,如一次会话中的表单步骤进度等。
-
IndexedDB
结构化本地数据库,支持大体积数据存储,适合缓存复杂结构的数据,比如离线文档、列表数据缓存、图片缓存等。
3. Service Worker 离线缓存
Service Worker 是一种运行在浏览器背后的独立线程,配合 Cache API,可以实现更高级的缓存控制和离线访问能力,是构建 PWA(渐进式 Web 应用)的核心技术之一。
四、渲染性能优化
渲染性能是指浏览器将 HTML、CSS、JavaScript 等资源转化为可视页面的能力。即便资源加载速度足够快,若渲染效率低下,依然会导致页面卡顿、滚动不流畅、动画掉帧等问题。渲染性能优化的目标是减少不必要的计算、绘制和布局重排,让浏览器更高效地完成页面呈现工作。
常见的优化方式包括控制重排重绘、使用高效的渲染方式、虚拟列表技术、避免冗余 DOM 等。
1. 减少重排与重绘
浏览器渲染流程中,每次修改 DOM 或 CSS 都可能引发「重排(Reflow)」或「重绘(Repaint)」,这两者都伴随性能开销,尤其是重排最为昂贵。过多或频繁的重排,会直接导致页面掉帧、卡顿。
-
避免频繁读写 DOM
JavaScript 中读取 DOM 信息(如
offsetHeight
、getBoundingClientRect
)后立即修改样式,会触发强制同步计算,阻塞渲染线程。应将 DOM 读写操作分批处理,避免交错执行。 -
合理使用 CSS 属性
使用
transform
和opacity
等属性修改样式,可避免触发重排,只会产生 GPU 层级上的合成操作,性能更优。例如:
css
/* 推荐 */
.fade-in {
transition: opacity 0.3s ease;
}
.move {
transform: translateX(100px);
}
/* 不推荐 */
.resize {
width: 100px; /* 会触发重排 */
}
-
动画使用 requestAnimationFrame
CSS 动画虽然流畅,但 JS 动画时应使用
requestAnimationFrame
来确保动画与浏览器刷新节奏同步,提升流畅性并避免资源浪费。
2. 虚拟列表与虚拟滚动
虚拟列表技术通过只渲染可视区域内的内容 ,大幅降低 DOM 节点数量。用来解决 页面中存在大量重复结构的 DOM(如长列表、数据表格等) 的问题
大致实现方案如下:
- React:
react-window
、react-virtualized
; - Vue:
vue-virtual-scroller
; - 自研方案:基于
scrollTop
和offsetHeight
手动控制渲染区间。
3. 控制 DOM 数量与嵌套层级
DOM 节点越多,浏览器构建渲染树的时间越长,布局计算复杂度越高。一般建议页面的有效 DOM 数量控制在 1500 以内,嵌套层级控制在 5 层以内。
4. 减少样式计算与复杂选择器
CSS 渲染过程中的「样式计算」会遍历 DOM 树与样式规则表,选择器越复杂,匹配效率越低。
- 避免过深的选择器,例如
.app .container .list .item .label
; - 避免使用通配符
*
和后代选择器div div div
; - 优先使用类选择器,且尽量不要使用 ID、属性选择器等难以复用的选择器。
五、性能监控与分析
前端性能优化不是一次性的工作,而是一个持续监测、评估、调整的过程。只有建立起完善的性能监控体系,及时发现并定位性能瓶颈,才能持续保障 Web 应用的高可用与高体验。因此,性能监控与分析在整个性能优化闭环中扮演着至关重要的角色。
性能监控分为两类:开发阶段的性能分析 和 上线后的实时监控。两者目标不同,但都必须具备。
1. 开发阶段:借助工具进行性能分析
在本地开发过程中,开发者可以通过浏览器提供的调试工具深入了解页面加载、渲染、脚本执行等各阶段的性能表现,从而发现瓶颈。常见的工具有:
-
Chrome DevTools:最常用的浏览器性能分析工具,提供以下核心功能模块:
- Performance 面板:可以录制并分析页面的加载过程,查看帧率、JS 执行耗时、布局重排次数、渲染瓶颈等信息。
- Lighthouse 面板:可一键生成页面性能报告,涵盖性能(Performance)、可访问性、SEO、最佳实践等多个维度,给出具体建议。
- Network 面板:查看资源加载时间、大小、状态码等,判断是否存在慢资源、请求阻塞、未压缩资源等问题。
- Coverage 面板:可检测 JS/CSS 代码的覆盖率,找出未使用的代码,为 Tree-Shaking 和包体积优化提供数据支撑。
-
WebPageTest / PageSpeed Insights:Google 提供的在线工具,适合分析线上站点的加载情况,支持不同网络环境模拟,并能针对 Core Web Vitals 提供评分和优化建议。
-
Core Web Vitals(核心指标):是 Google 推出的关键性能指标,衡量真实用户体验的关键维度,包括:
- LCP(Largest Contentful Paint):最大内容绘制时间,反映首屏加载速度;
- FID(First Input Delay):首次输入延迟,衡量交互响应性;
- CLS(Cumulative Layout Shift):累积布局偏移,衡量页面稳定性。
优秀的页面应该满足以下标准:
- LCP < 2.5s
- FID < 100ms
- CLS < 0.1
2. 上线阶段:实时性能监控与上报
当页面上线后,由于用户设备、网络、行为差异极大,仅靠本地测试无法全面掌握性能表现。因此需要接入性能监控系统,在真实用户环境中收集关键性能数据,以便持续追踪和告警。
-
自定义监控 SDK,可以通过
PerformanceObserver
、performance.getEntriesByType()
等 Web API 收集以下指标:- 页面加载耗时(DNS、TCP、TTFB、DomContentLoaded、Load)
- 资源加载耗时(图片、脚本、字体等)
- 首屏渲染时间(使用打点或 MutationObserver 辅助采集)
- 首次交互时间、输入延迟
- JS 错误、Promise 未处理异常、接口异常
- 帧率、卡顿检测(使用
requestAnimationFrame
检测长任务)
将这些数据结构化后,统一上报到日志服务器,存入数据库用于可视化展示。
- 接入第三方平台,例如:
- 阿里云 ARMS 前端监控
- 百度 ESee / 字节跳动 Eeyes
- 腾讯 TARS Frontend Monitor
- Sentry / LogRocket / Datadog(国外)
- 性能波动与灰度分析:如某次发版后 LCP 从 1.8s 突然上升到 4s。此时可以结合版本号、地域、终端等维度回溯问题来源,快速定位性能回退的根因。
3. 建立性能指标体系与告警机制
比如:
- 在每次构建后自动跑 Lighthouse 分析;
- 设置性能阈值(如 LCP 超过 3s 告警);
- 页面 JS 体积超过 250KB 即阻断上线流程;
- 配合灰度发布与版本对比,确保性能不回退。
- 等等
除了以上 5 点之外,咱们还需要注意 代码层面 的一些优化,比如:减少代码体积,逻辑尽量清晰 等等。不过这一部分的内容更多会因人而异,所以这里就不去展开说啦