目录
前言
互联网发展到今日,用户体验成为网站优劣评判的重要指标,用户不再停留在“能用即可”的阶段,而开始追求“更快,更美”。前端是最贴近于用户侧,所以前端性能优化就显得尤为重要。本文将介绍衡量前端性能的一些指标,并提出对应的优化方案,希望能对开发者有所帮助。
Apdex指数
在介绍指标之前,我们先了解一下Apdex指数(Application Performance Index,应用性能指数),它是用户对应用性能满意度的一个量化值,分布在0-1之间:
- 0 - 没有满意用户
- 1 - 所有用户都满意
0.7 是一个默认标准,表示网站性能良好,同样,这个值的标准也完全由你自己来定,要求高的话就定为更高的值,例如 0.85。
计算公式:
Apdex 指数 = ( 满意数量 + 0.5 * 可容忍数量 ) / 总样本数
一些指标
上图演示了浏览器加载页面的详细过程,其中每个节点的值可以通过js代码获取
console.log(performance.timing)
阶段性指标
字段 | 描述 | 计算方式 | 备注 |
---|---|---|---|
unload | 前一个页面卸载耗时 | unloadEventEnd - unloadEventStart | 前一个页面卸载时可能监听了 unload 做些数据收集,会影响页面跳转 |
redirect | 重定向耗时 | redirectEnd - redirectStart | 过多重定向影响性能 |
appCache | 缓存耗时 | domainLookupStart - fetchStart | |
dns | DNS 解析耗时 | domainLookupEnd - domainLookupStart | |
tcp | TCP 连接耗时 | connectEnd - connectStart | |
ssl | SSL 安全连接耗时 | connectEnd - secureConnectionStart | 只在 HTTPS 下有效 |
ttfb | Time to First Byte(TTFB),网络请求耗时 | responseStart - requestStart | |
response | 数据传输耗时 | responseEnd - responseStart | |
dom | 可交互 DOM 解析耗时 | domInteractive - responseEnd | Interactive content |
dom2 | 剩余 DOM 解析耗时 | domContentLoadedEventStart - domInteractive | DOMContentLoaded 所有DOM元素都加载完毕(除了 async script) |
DCL | DOMContentLoaded 事件耗时 | domContentLoadedEventEnd - domContentLoadedEventStart | document.addEventListener(‘DOMContentLoaded’, cb) |
resources | 资源加载耗时 | loadEventStart - domContentLoadedEventEnd | 完整DOM(DOMContentLoaded)到资源加载完毕(window.onLoad)时间 |
onLoad | onLoad事件耗时 | loadEventEnd - loadEventStart |
关键性指标
字段 | 描述 | 计算方式 | 备注 |
---|---|---|---|
firstbyte | 首包时间 | responseStart - domainLookupStart | |
fpt | First Paint Time, 首次渲染时间 / 白屏时间 | responseEnd - fetchStart | 从请求开始到浏览器开始解析第一批 HTML 文档字节的时间差 |
tti | Time to Interact,首次可交互时间 | domInteractive - fetchStart | 浏览器完成所有 HTML 解析并且完成 DOM 构建,此时浏览器开始加载资源 |
ready | HTML 加载完成时间, 即 DOM Ready 时间 | domContentLoadedEventEnd - fetchStart 如果页面有同步执行的 JS,则同步 JS 执行时间 = ready - tti | |
load | 页面完全加载时间 | loadEventStart - fetchStart | load = 首次渲染时间 + DOM 解析耗时 + 同步 JS 执行 + 资源加载耗时 |
-
FP 白屏(First Paint Time ): 从页面开始加载到浏览器中检测到渲染(任何渲染)时被触发(例如背景改变,样式应用等)
白屏时间过长,会让用户认为我们的页面不能用或者可用性差
-
FCP 首屏(first contentful paint ):从页面开始加载到页面内容的任何部分呈现在屏幕上的时间。
关注的焦点是内容,这个度量可以知道用户什么时候收到有用的信息(文本,图像等),通常情况下,FP和FCP值是一样的
-
FMP 首次有效绘制(First Meaningful Paint ): 表示页面的“主要内容” 开始出现在屏幕上的时间点,这项指标因页面逻辑而异,因此上不存在任何规范。
-
LCP (Largest Contentful Paint ):LCP 指标代表的是视窗最大可见图片或者文本块的渲染时间。
-
长任务(Long Task):当一个任务执行时间超过50ms 时消耗到的任务
50ms 阈值是从RAIL模型总结出来的结论,这个是google研究用户感知得出的结论,类似永华的感知/耐心的阈值,超过这个阈值的任务,用户会感知到页面的卡顿
-
TTI (Time To Internative):从页面开始到它的主要子资源加载到能够快速地响应用户输入的时间。(没有耗时长任务)
-
首次输入延时FID (first Input Delay):从用户第一次与页面交互到浏览器实际能够开始处理事件的时间。(点击,输入,按键)
浏览器的Performance怎么使用
主要有4方面:(对照下图)
1.控制按钮。
2.overview。页面性能的高级汇总(FPS:帧率,CPU:CPU占用,NET:网络请求)
3.火焰图。CPU堆叠追踪的可视化(左侧名称是主线程的各种事件,Network:网络请求详细情况)
4.数据统计。以图表的形式汇总数据(summary:统计报表,Bottom-Up:事件时长顺序,Call Tree:事件调用顺序,Event log:事件发生的先后顺序)
颜色表示:HTML 文件为蓝色,脚本为黄色,样式表为紫色,媒体文件为绿色,其他资源为灰色
-
控制按钮区域:
打开想要查看的页面,刷新页面,打开performance面板,点击Record按钮(当前面板左上角),开始录制,recode按钮变为红色,执行页面交互,再次点击Record停止录制。完成录制后,开发者工具会猜测哪一部分记录最相关,并自动缩放到那一部分 -
overview窗格:
a. FPS:每秒帧数。绿色竖线越高,说明每秒展示的帧数越多,越流畅,相对用户体验就越好。FPS图表上的红色块表示长时间帧,很可能会出现卡顿。
b. CPU:CPU资源。此面积图指示消耗 CPU 资源的事件类型(蓝色代表加载事件loading,黄色代表脚本运算事件scripting,紫色代表渲染事件rendering,绿色代表绘制事件painting)
c. NET:每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)
d. HEAP: 堆内存占用量
鼠标可以点击或拖动选取某一部分,火焰图自动匹配到相同部分,可以用 W(放大)、S(缩小)、A(左移)、和 D(右移) 键调整选择。 -
火焰图:
-
Network:表示每个服务器资源的加载情况。
-
Frames:表示每幅帧的运行情况。
-
Timings:上图中有 4 条虚线,分别表示如下。
- DCL(DOMContentLoaded)表示 HTML 文档加载完成事件。当初始 HTML 文档完全加载并解析之后触发,无需等待样式、图片、子 frame 结束。作为明显的对比,load 事件是当个页面完全被加载时才触发。
- FP(First Paint)首屏绘制,页面刚开始渲染的时间。
- FCP(First Contentful Paint)首屏内容绘制,首次绘制任何文本,图像,非空白canvas 或 SVG 的时间点。
- FMP(First Meaningful Paint)首屏有意义的内容绘制,这个“有意义”没有权威的规定,本质上是通过一种算法来猜测某个时间点可能是 FMP。有的理解为是最大元素绘制的时间,即同LCP(Largest Contentful Paint )。
其中 FP、FCP、FMP 是同一条虚线,三者时间不一致。比如首次渲染过后,有可能出现 JS 阻塞,这种情况下 FCP 就会大于 FP。 - L(Onload)页面所有资源加载完成事件。
- LCP(Largest Contentful Paint )最大内容绘制,页面上尺寸最大的元素绘制时间。
-
Main:表示主线程。
- Javascript 的计算与执行;
- CSS 样式计算;
- Layout 布局计算;
- 将页面元素绘制成位图(paint),也就是光栅化(Raster);
- 将位图给合成线程。
-
Raster:光栅化(处理光栅图,即位图)。
-
GPU:表示 GPU 占用情况。
-
Chrome_childIOThread:子线程。
-
Compositor:合成线程。
- 将位图(GraphicsLayer 层)以纹理(texture)的形式上传给 GPU;
- 计算页面的可见部分和即将可见部分(滚动);
- CSS 动画处理;
- 通知 GPU 绘制位图到屏幕上。
-
-
数据统计与汇总
- Summary:表示各指标时间占用统计报表;
- Bottom-Up:表示事件时长排序列表(倒序);
- Call tree:表示事件调用顺序列表;
- Event Log:表示事件发生的顺序列表;
给你的网站打个分?---- LightHouse的使用
平时对于网站的性能评判,我们可以使用一些监控软件,殊不知,Google自带的这个LightHouse简直不要太好用。
- 右侧框框内可以选择想要打分的项目及网站的pc端还是h5端。
- 点击生成报告,就可以看到你网站的总分及每项细分(这里以csdn为例,没想到他们这么低 【手动汗颜】)
- 性能(Performance)
- 访问无障碍(Accessibility)
- 最佳实践(Best Practice)
- 搜索引擎优化(SEO)
- PWA(Progressive Web App)
点击每一项可以看到分别不同的评分,下拉可以看到具体哪些原因导致你网站评分低
也可以通过右侧更多按钮生成HTML或JSON文件
优化方案
1. 减少HTTP请求
同样的展示效果,如果文件太多,就需要多次建立连接关闭连接,增加耗时。建议将多个小文件合并为一个大文件,减少HTTP请求次数,从而提高效率。
具体操作:
- 使用webpack、gulp等打包工具对代码进行打包压缩;
- 使用 css sprite(精灵图)将多个小图片放到一张或使用 font-awesome 将小图标生成矢量字体文件,只请求一次;
2. 使用 HTTP2
- 解析速度快:服务器解析 HTTP1.1 的请求时,必须不断地读入字节,直到遇到分隔符 CRLF 为止。而解析 HTTP2 的请求就不用这么麻烦,因为 HTTP2 是基于帧的协议,每个帧都有表示帧长度的字段。
- 多路复用:HTTP1.1 如果要同时发起多个请求,就得建立多个 TCP 连接,因为一个 TCP 连接同时只能处理一个 HTTP1.1 的请求。在 HTTP2 上,多个请求可以共用一个 TCP 连接,这称为多路复用。
- 首部压缩:HTTP2可以对header建表建索引,对相同的header使用索引进行传输,更加快捷。
- 优先级:HTTP2 可以对比较紧急的请求设置一个较高的优先级,服务器在收到这样的请求后,可以优先处理。
- 服务端推送:HTTP2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。也就是说,客户端想要及时得到服务端的状态,再也不用定时发轮讯请求了,使用HTTP2,当服务端数据变化后直接通知客户端。
3. 使用服务端渲染
- 客户端渲染: 获取 HTML 文件,根据需要下载 JavaScript 文件,运行文件,生成 DOM,再渲染。
- 服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML。
4. 静态资源使用 CDN
内容分发网络(CDN)是一组分布在多个不同地理位置的 Web 服务器。我们都知道,当服务器离用户越远时,延迟越高。CDN 就是为了解决这一问题,在多个位置部署服务器,让用户离服务器更近,从而缩短请求时间。
5. 将 CSS 放在文件头部,JavaScript 文件放在底部
所有放在 head 标签里的 CSS 和 JS 文件都会堵塞渲染。如果这些 CSS 和 JS 需要加载和解析很久的话,那么页面就空白了。所以 JS 文件要放在底部,等 HTML 解析完了再加载 JS 文件。
那为什么 CSS 文件还要放在头部呢?
因为先加载 HTML 再加载 CSS,会让用户第一时间看到的页面是没有样式的、“丑陋”的,为了避免这种情况发生,就要将 CSS 文件放在头部了。
另外,JS 文件也不是不可以放在头部,只要给 script 标签加上 defer 属性就可以了,异步下载,延迟执行。
6. 善用缓存,不重复加载相同的资源
使用强制缓存或协商缓存策略对静态文件进行缓存。
7. 图片优化
- 图片延迟加载,图片标签出现在页面上再对src赋值,进行图片的加载。
- 响应式图片,对不同分辨率展示不同大小的图片。
- 降低图片质量,例如 JPG 格式的图片,100% 的质量和 90% 质量的通常看不出来区别,尤其是用来当背景图的时候。我经常用 PS 切背景图时, 将图片切成 JPG 格式,并且将它压缩到 60% 的质量,基本上看不出来区别。
- 尽可能利用 CSS3 效果代替图片,有很多图片使用 CSS 效果(渐变、阴影等)就能画出来,这种情况选择 CSS3 效果更好。因为代码大小通常是图片大小的几分之一甚至几十分之一。
- 使用 webp 格式的图片,WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,但是兼容性不是很好。
8. 减少重绘和回流
- 重绘:当重新生成渲染树后,就要将渲染树每个节点绘制到屏幕,这个过程叫重绘。
- 回流:当改变 DOM 元素位置或大小时,会导致浏览器重新生成渲染树,这个过程叫回流。
- 不是所有的动作都会导致重排,例如改变字体颜色,只会导致重绘。记住,重排会导致重绘,重绘不会导致重排 。
- 如何减少重排重绘?
- 用 JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式。
- 如果要对 DOM 元素执行一系列操作,可以将 DOM 元素脱离文档流,修改完成后,再将它带回文档。推荐使用隐藏元素(display:none)或文档碎片(DocumentFragement),都能很好的实现这个方案。
总结
这块东西还是挺多的,本文只是摘取了各方面的一部分,如果想更加细致研究,可以针对每一部分展开研究,附上各位大佬的文档【手动抱拳】。