如何处理加载时的性能问题和运行时的性能问题
好的,这是一个非常实战性的话题。我将从 “如何处理” 的角度,为你提供一套清晰的、可操作的解决方案和最佳实践。
处理性能问题遵循一个通用流程:测量 → 定位 → 优化 → 验证。下面我们分别针对加载时和运行时的问题展开。
一、处理加载时性能问题
加载问题的核心在于更快地交付关键资源,让用户尽可能早地看到页面并与之交互。
第一步:测量与定位
使用工具量化问题,找到瓶颈。
- Lighthouse / PageSpeed Insights: 生成报告,获取FCP、LCP、TTI等指标分数和优化建议。
- Chrome DevTools - Network面板: 录制并分析“瀑布流”,查看:
- 哪些资源加载最慢、体积最大?
- TTFB(首字节时间)是否过长?
- 是否存在排队或停滞时间?
- WebPageTest: 进行多地点、多网络环境下的深度分析。
第二步:实施优化方案
1. 优化资源体积
- JavaScript & CSS:
- 压缩与混淆: 使用
TerserWebpackPlugin(JS) 和CssMinimizerWebpackPlugin(CSS) 等工具。 - Tree Shaking: 确保打包工具(如Webpack)能移除未使用的代码。
- 代码分割: 使用
import()语法进行动态导入,实现路由级分割和组件级分割,避免首次加载不必要的代码。
- 压缩与混淆: 使用
- 图片:
- 格式选择: 使用现代格式(WebP/AVIF),并为不支持的老浏览器提供JPEG/PNG回退(使用
<picture>标签)。 - 压缩: 使用工具(如Squoosh、Imagemin)对图片进行有损或无损压缩。
- 响应式图片: 使用
srcset和sizes属性,为不同屏幕尺寸提供不同大小的图片。 - 懒加载: 对非首屏图片使用
loading="lazy"属性。
- 格式选择: 使用现代格式(WebP/AVIF),并为不支持的老浏览器提供JPEG/PNG回退(使用
2. 优化网络请求
- 减少请求数量:
- HTTP/2: 启用HTTP/2,利用其多路复用特性,减少合并小文件的需求。
- 资源合并: 在HTTP/1.1环境下,可以考虑合并CSS/JS小文件。
- 利用浏览器缓存:
- 强缓存: 为不常变化的资源(如第三方库、图片)设置
Cache-Control: max-age=31536000(一年)。 - 协商缓存: 为可能变化的资源(如CSS/JS文件)设置
ETag或Last-Modified。 - Service Worker: 实现更精细的离线缓存和网络请求控制。
- 强缓存: 为不常变化的资源(如第三方库、图片)设置
3. 消除渲染阻塞
- CSS:
- 内联关键CSS: 将首屏渲染所必需的关键样式直接内嵌在
<head>中。 - 异步加载非关键CSS: 使用
preload或media="print" onload="this.media='all'"技巧异步加载其余样式。
- 内联关键CSS: 将首屏渲染所必需的关键样式直接内嵌在
- JavaScript:
- 使用
defer或async:defer:脚本异步下载,在HTML解析完成后、DOMContentLoaded事件触发前按顺序执行。async:脚本异步下载,下载完成后立即执行,可能会阻塞渲染。适用于无依赖的独立脚本(如分析统计代码)。
- 使用
4. 优化服务端响应
- 减少TTFB:
- CDN: 使用内容分发网络,将静态资源分发到离用户更近的节点。
- 后端缓存: 对数据库查询结果或完整的HTML页面进行缓存(如使用Redis、Varnish)。
- 优化后端代码: 检查并优化慢查询、低效算法。
二、处理运行时性能问题
运行时问题的核心在于保持主线程的轻盈,确保用户交互(点击、滚动、动画)的流畅响应。
第一步:测量与定位
- Chrome DevTools - Performance面板:
- 录制用户操作(如滚动、按钮点击)。
- 分析时间线,寻找长任务(红色标记,>50ms)、强制同步布局(紫色“Layout”块频繁出现)和掉帧的FPS图表。
- Chrome DevTools - Memory面板: 拍摄堆快照对比,查找内存泄漏。
- Chrome DevTools - Performance Monitor: 实时观察JS堆大小、DOM节点数、FPS等指标的变化趋势。
第二步:实施优化方案
1. 优化JavaScript执行
- 分解长任务:
- 将长时间运行的函数拆分成多个小块。
- 使用
setTimeout或setInterval进行分时操作。 - 使用
requestIdleCallback在浏览器空闲时期执行低优先级的任务。
- 使用 Web Workers:
- 将复杂的、与UI无关的计算任务(如图像处理、数据排序)移至Web Worker,避免阻塞主线程。
- 优化事件监听器:
- 使用防抖或节流 优化高频率触发的事件(如
scroll,resize,input)。 - 避免在事件处理函数中执行重操作。
- 使用防抖或节流 优化高频率触发的事件(如
2. 避免强制同步布局
- 批量化DOM读写操作:
- 错误示范(触发多次布局):
let width = element.offsetWidth; // 读 -> 触发布局 element.style.width = width + 10 + 'px'; // 写 -> 再次触发布局 let height = element.offsetHeight; // 读 -> 再次触发布局 element.style.height = height + 10 + 'px'; // 写 -> 再次触发布局 - 正确示范(批量读写,一次布局):
let width = element.offsetWidth; // 读 let height = element.offsetHeight; // 读 element.style.width = width + 10 + 'px'; // 写 element.style.height = height + 10 + 'px'; // 写 - 使用
requestAnimationFrame来集中进行样式修改,使其与浏览器刷新率同步。
- 错误示范(触发多次布局):
3. 优化渲染性能
- 使用
transform和opacity创建动画:- 这两个属性由合成器线程处理,不会触发主线程的布局和绘制,效率极高。
- 优先使用:
transform: translate/scale/rotate,opacity。 - 避免使用: 会改变元素几何属性的属性,如
width,height,top,left。
- 减少重绘与重排:
- 避免频繁修改会引发重排的样式。
- 使用
will-change属性小心地提示浏览器哪些元素将会变化。
4. 预防内存泄漏
- 及时清理:
- 定时器: 使用
clearInterval和clearTimeout清除不再需要的定时器。 - 事件监听器: 在组件销毁或节点移除时,使用
removeEventListener移除所有绑定的事件监听器。在React/Vue中,利用生命周期(如componentWillUnmount)或Effect清理函数。 - DOM引用: 移除DOM节点后,将其引用设置为
null。 - 全局变量: 避免意外创建全局变量。
- 定时器: 使用
总结:性能优化清单
你可以将以下清单作为你项目优化的检查表:
加载时清单
运行时清单
记住,性能优化是一个持续的过程,而不是一次性的任务。通过将上述实践融入到你的开发流程中,并定期使用工具进行审计,你就能系统地提升应用的性能表现。
挣钱养家

浙公网安备 33010602011771号