影响页面性能的因素
- 资源加载:js、样式、图片等资源
- 浏览器渲染:不合理的 DOM 操作会导致页面的重排和重绘,频繁触发会导致性能下降。关注浏览器渲染原理和性能指标,可以帮助我们优化页面渲染。
- 网络请求:资源预加载、DNS预解析。
- 缓存策略
1.资源加载
首先来了解两个时间点:
DOMContentLoaded
当 HTML 已经完成解析,并且构建出了 DOM,但此时外部资源比如样式和脚本可能还没加载完成,并且该事件需要绑定到 document 对象上
onLoad
当页面所有资源(包括 CSS、JS、图片、字体、视频等)都加载完成才触发,而且它是绑定到 window 对象上
阻塞资源,影响 DOM 解析
- 非 defer/async 的 js,会阻塞后面的 DOM 解析,defer 会在 DOMContentLoaded 之前执行,async 则是什么时候下载完了什么时候执行。
- css 不阻塞DOM解析,但会阻塞(css 后面的)DOM渲染,因为渲染依赖 DOM 和 CSSOM。为了避免阻塞,应该把样式放到 html 最底下。同时,css 还会阻塞后面的 js 执行,因为 js 可能会访问样式。因此,当 css 后面有 js 的时候,css 的加载会影响 DOM 解析。
对于阻塞资源,应该进行代码压缩、tree shaking等;优化资源加载顺序,优先加载最重要的资源。
非阻塞资源,不影响 DOMContentLoaded,但影响 onLoad
图片、字体、媒体文件等,过大的资源体积会影响下面说到的性能考察指标。
图片是页面中最重要的资源之一,通过渐进式 jpeg、loading="lazy" 懒加载、合成雪碧图、转换图片为webp格式、压缩图片等方法优化图片加载。
其他资源也可以用类似的方式来优化加载速度。
2.浏览器渲染
谈到资源阻塞渲染,避免回流(重排),重绘等等,就不得不提到浏览器的工作原理。
常见面试题:浏览器从输入url到页面渲染发生了什么?关键渲染路径是浏览器渲染的核心部分,其他前置部分包括DNS查询,TCP握手,TLS协商,请求响应等。
// 关键渲染路径 CRP
html => DOM ↘️
render tree => layout => paint
css => CSSOM ↗️ (reflow重排)(重绘)
- 重排:更新了元素的几何属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。
- 重绘:更新元素的绘制属性,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
使用 LightHouse 进行性能检测
知道了限制浏览器性能的因素,还要对页面性能进行客观的检测。LightHouse 就是这样的一个工具。
打开你需要检测的页面,打开开发工具,点击LightHouse标签,进行性能检测。同时查看 proformance 标签,分析浏览器在加载渲染页面时候究竟发生了什么。
LightHouse 有6个考察指标:
- First Contentful Paint(FCP): 首次内容绘制:页面上「第一个元素」在屏幕上完成渲染的时间。
- Time to Interactive(TTI): 可交互时间:页面从开始加载到主要子资源完成渲染,并能够快速(50ms内)、可靠地响应用户输入所需的时间。
- Speed Index(SI): 速度指数:是一种衡量页面可视区域加载速度的指标。
- Total Blocking Time(TBT): 总阻塞时长:指的是 FCP 与 TTI 之间主线程被阻塞的总时长。单个长任务阻塞时间 = 长任务时间 - 50ms,TBT 为这些阻塞时间的总和。即当一个任务执行时间超过50ms,则认为是阻塞时间,应当尽量避免单个任务执行时间过长。
- Largest Contentful Paint(LCP): 最大内容绘制,指页面上最大的图片或文字绘制的时间点。在不同页面上可能是不同的元素。
- Cumulative Layout Shift(CLS): 累积布局偏移,测量的是整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移分数。每当一个可见元素的位置从一个已渲染帧变更到下一个已渲染帧时,就发生了布局偏移 。比如文本在毫无预警的情况下移位,导致找不到先前阅读的位置。或者正要点击一个链接或一个按钮,但在手指落下的瞬间,链接移位了,点到了别的东西。
3.网络请求
网络请求的优化不仅是前端的工作,还有运维和后端的工作。比如以下的优化:
- 减小 Cookie 的体积
- 服务端开启 Gzip 压缩
- 使用 CDN
DNS预解析
<link rel="dns-prefetch" href="//yourwebsite.com">
预先解析之后会访问到的域名。
资源预加载
<link rel="prefetch" href="main.js" as="script">
prefetch (预取用),它可以利用浏览器的空闲时间来预取用(下载)用户可能在不久的将来会访问的资源。加载完成后浏览器不会马上解析资源,而只是缓存到本地。
<link rel="preload" href="style.css" as="style">
preload (预加载),它告诉浏览器如何将特定资源提前提取到当前页面中。本质上,它会在当前页面开始加载之前在浏览器后台提前下载资源。这些资源应该是马上就要用到的,提前对这些资源进行预先并行下载;否则就会在dom解析过程中串行地进行下载、解析、执行的过程;节省了dom解析过程中的资源下载时间。
preload 和 prefetch 还支持预加载其它域名的资源,需要设置 crossorigin 属性到 标签。
4.缓存策略
- 在 Header 内的字段用于控制缓存机制
- 老方法 Expires,记录的绝对值
- 新方法 Cache-Control 多了一堆选项,记录的时间是相对值
- 获取缓存检测缓存是否过期,如果没过期取缓存,优先从内存,其次硬盘,如果过期,则与服务器协商缓存是否仍然可用,如果不可用则获取,可用取缓存 彻底理解浏览器的缓存机制
参考文章 Web 性能