参考文档:
浏览器工作原理与实践—李兵
http://www.zyiz.net/tech/detail-134416.html
当打开一个页面,用户可能会看到这样一个变化过程:白屏 → 打底图 → 出现部分内容 → 首屏内容全部出现,但图片还在加载中 → 首屏内容全部出现,图片也已经加载完成。
一、Web性能指标
Web性能指标.png1 Time to First Byte(TTFB):第一字节时间
是指浏览器从请求页面开始到接收第一字节的时间,这个时间段内包括 DNS 查找、TCP 连接和 SSL 连接。
2. First Paint(FP ):首次绘制时间
是指浏览器从开始加载到首次绘制像素
到屏幕上的时间节点,也就是页面在屏幕上首次发生视觉变化的时间,也称白屏时间
在渲染进程确认要渲染当前的请求后,渲染进程会创建一个空白页面,等待资源加载,如果 FP 时间过久,说明页面的 HTML 文件可能由于网络原因导致加载时间过久。
3. First Contentful Paint(FCP ): 首次页面内容绘制时间
是指浏览器首次绘制来自 DOM 的内容的时间节点,内容必须是文本、图片(包含背景图)、非白色的 canvas 或 SVG,也包括带有正在加载中的 Web 字体的文本。
这是用户第一次开始看到页面内容。
4. Largest Contentful Paint(LCP):可视区域中最大的内容绘制时间
是指浏览器屏幕可视区域中最大的内容元素呈现到屏幕上的时间,用以估算页面的主要内容对用户可见时间。
5. Speed Index(SI): 页面可视区域中内容的填充速度的指标
6. First Screen Paint(FSP):首屏内容绘制时间
是指页面从开始加载到首屏内容全部绘制完成的时间,用户可以看到首屏的全部内容。
如果说 LCP 是用户看到有效内容的最近似的时间,那么在 FSP 这个时间点用户已经看到了可视区域内完整的内容,可以说是衡量用户视觉体验最合适的指标。
7. First CPU Idle(FCI),用户第一次
是指用户可以开始与第一次页面交互的时间节点。
8. Time to Interactive(TTI):交互完全响应时间
它表示页面中所有元素都达到了完全可交互的时间节点。
简单理解就这时候页面的内容已经完全显示出来了,所有的 JavaScript 事件已经注册完成,页面能够对用户的交互做出快速响应。
如果要解决 TTI 时间过久的问题,我们可以推迟执行一些和生成页面无关的 JavaScript 工作。
DOMContentLoad 和 onload的区别?
-
DOMContentLoad,当 **HTML 文档被完全加载和解析完成生成DOM后之后触发,无需等待样式表、图像和子框架加载完成。
-
onload, 页面所有资源都加载完毕后(比如图片,CSS),才被触发。
二、Chrome浏览器的Web 性能检测工具
1. Lighthouse
Lighthouse 相对简单,它会分析检测到的性能数据并给出站点的性能得分,同时,还会给我们提供一些优化建议。
Lighthouse界面 .png我们可以看到,在生成报告之前,我们需要先配置 Audits,配置模块主要有两部分组成,一个是监测类型 (Categories),另外一个是设备类型 (Device)。
Categories(监控类型)部分: 是指需要监控哪些内容
它们的功能分别是:
-
Performance,监测并分析 Web 性能 ;
-
Progressive Web App,监测并分析 PWA(渐进式Web 应用) 程序的性能;
-
Best practices,监测并分析 Web 应用是否采用了最佳实践策略 ;
-
Accessibility,监测并分析是否实施了无障碍功能;
-- 无障碍功能是指是为身体有障碍的人提供便捷访问Web 应用的功能 -
SEO,监测并分析 Web 应用是否采实施了 SEO 搜素引擎优化 。
Device(设备 ) 部分
Moblie 选项是用来模拟移动设备环境的;
Desktop 选项是用来模拟桌面环境的。
点击Analyze page load 就开始生成报告了,如下图:
Lighthouse性能分析报告 .png
图中中间圆圈中的数字表示该站点在加载过程中的总体 Web 性能得分,满分100分。
Lighthouse 除了生成性能指标以外,还会分析该站点并提供了很多优化建议,如性能指标 (Metrics)、可优化项 (Opportunities)、手动诊断 (Diagnostics),以及运行时的一些基本数据(PASSED AUDITS )。
Lighthouse 只能监控加载阶段的性能数据。
性能指标中的Speed Index是首屏时间,即我们常说的LCP (Largest Contentful Paint)首屏绘制完成时间。
2. Performance
Performance 只是单纯地采集性能数据,并将采集到的数据按照时间线的方式来展现,需要我们自己去做性能分析。
Performance 不仅可以监控加载阶段的性能数据, 还可以监控交互阶段的性能数据。
Performance 性能监控.png通常,我们通过概览面板来定位到可能存在问题的时间节点,但是要分析导致该问题的原因,就需要查看性能面板了。
使用概览面板定位问题.png性能面板
Main 指标,记录了渲染进程的主线程的任务执行情况;
Main 指标.png如图,一段段横条代表执行一个个任务,长度越长,花费的时间越多;竖向代表该任务的执行记录;你可以把任务看成是一个 函数,在执行这个 函数的过程中,它会调用一系列的子函数(也就是每条横线下竖向记录),这些子函数就是每个任务的执行过程。
借助Main 指标可以分析页面的加载流程。
Main 指标.png-
首先 Send request,该过程表示网络请求已被发送。然后该任务进入了等待状态;
-
接着由网络进程负责下载资源,当接收到响应头的时候,该任务便执行 Receive Respone 过程,该过程表示接收到 HTTP 的响应头了;
-
接着执行 DOM 事件:pagehide、visibilitychange 和 unload 等事件,如果你注册了这些事件的回调函数,那么这些回调函数会依次在该任务中被调用;
-
DOM 事件执行完成之后,就开始接收 HTML 数据了,这体现在了 Recive Data 过程;
-- Recive Data 过程表示请求的数据已被接收,如果 HTML 数据过多,会存在多个 Receive Data 过程 -
等到所有的数据都接收完成之后,渲染进程会触发Finish load ,该过程表示网络请求已经完成;
-
数据都接收完成之后,便进入 HTML 解析阶段(HTMLParser);
-- 在 ParserHTML 的过程中,如果解析到了 script 标签,那么便进入了脚本执行过程,也就是图中的 Evalute Script;
-- 执行一段脚本时,首先会进行编译,于是在 Evalute Script 过程中,先进入了脚本编译过程,也就是图中的 Complie Script;
-- 脚本编译好之后,就进入程序执行过程,执行全局代码时,V8 会先构造一个 anonymous 过程,在执行 anonymous 过程中,会调用 setNewArea 过程,setNewArea 过程中又调用了 createElement,触发 DOM 内容的修改,所以又强制执行了 ParserHTML 过程生成的新的 DOM。 -
DOM 生成完成之后,会触发相关的 DOM 事件,如 DOMContentLoaded,还有 readyStateChanged;
-
然后ParserHTML 过程继续计算样式表,即 Reculate Style,生成 CSSOM;
-
在生成完了 DOM 和 CSSOM 之后,渲染主线程首先会执行了一些 DOM 监听事件,如 readyStateChange、load、pageshow;
-
有了DOM 和 CSSOM 之后,开始进入布局阶段,这个过程对应图中的 Layout;
-- 根据DOM 和 CSSOM 生成布局树(LayoutTree),
-- 根据布局树进行布局计算,计算出布局树的每个节点在浏览器屏幕上的具体坐标位置。 -
根据布局计算结果更新图层树 (LayerTree),这个过程对应图中的 Update LayerTree ;
-
有了图层树之后,染引擎会对图层树中的每个图层进行绘制,生成待【绘制列表】,这个过程就称为 Paint;
13.有了绘制列表,把绘制列表交给合成线程,就会进入栅格化节点,生成位图,这个过程对应图中的 Composite Layers;
-
合成线程维护了一个 Raster 线程池,线程池中的每个线程称为 Rasterize,用来执行光栅化操作,对应的任务就是 Rasterize Paint。由于光栅化操作是在 GPU 进程中执行的,因此 Rasterize 线程需要和 GPU 线程保持通信。
-
然后 GPU 生成图像,GPU进程根据不同图块生成位图,还给合成线程,合成线程将图层提交给浏览器进程,
-
浏览器进程将其合成并生成页面,最后再将页面显示在屏幕上。
Compositor 指标,记录了合成线程的任务执行过程;
GPU 指标,记录了 GPU 进程主线程的任务执行过程;
Chrome_ChildIOThread 指标,对应的就是 IO 线程的任务记录;
Network 指标,网络记录展示了页面中的每个网络请求所消耗的时长;
Timings 指标,用来记录一些关键的时间节点在何时产生的数据信息,如 FP、FCP、LCP 等;
Interactions 指标,用来记录用户交互操作,比如点击鼠标、输入文字等交互信息。
三、前端性能优化具体措施
通过 对Performance中Main 指标的分析,我们可以在整个页面请求到显示过程中发现页面的性能瓶颈,然后就可以有针对性地去进行优化。
1. 让资源体积变小,减少响应时间
- 资源压缩
- 资源合并
- API 优化
2. 使用缓存手段,让下载时间更快
- 利用CDN实现缓存或者就近提供资源
-- 使用强制缓存减少HTTP请求,配合使用协商缓存提升响应速度,资源复用
3. 使用预加载和并行加载,打破时间窗口
4. 降低渲染线程的计算复杂度
- 优化代码质量
- 减少重排、重绘
- 节流、防抖
5.使用SPA单页面应用,可以很好的解决二跳性能及用户体验问题
缺点
- 不利于大型团队
- 不利于SEO
- 内存管理成本更高
6. 避免使用 table 布局
-- table必须在页面完全加载后才显示, table布局会阻挡浏览器加载的速度和渲染顺序
-- 层级嵌套深,使用大量的 td tr,代码冗余量大
-- 灵活性差,无法进行布局修改
-- 语义化角度难以理解
长列表虚拟滚动
四、重排 vs 重绘
1. 重排
是指修改了元素几何属性,如位置、尺寸、内容、结构等变化,引发元素几何位置变化,浏览器需要重新计算样式、构建布局树,开始之后的一系列子阶段,这个过程就叫重排。
重排需要更新完整的渲染流水线,所以开销比较大。
触发重排的情况:
- 添加或删除可见的DOM元素
- 元素位置改变
- 元素尺寸改变
- 元素内容改变
- 页面渲染器初始化
- 浏览器窗口大小发生改变
减少重排的方式:
- 使用文档片段DocumentFragment,减少DOM操作,将多次操作DOM合并为一次
//优化前,每次插入新的节点都会造成一次重排
let myList = document.querySelector('.my-list');
for(let i =0; i < 10; i++){
let li = document.createElement('li');
li.innerText = i;
myList.appendChild(li); // 每插入一个新节点都会造成一次重排
}
//优化后,使用文档片段(虚拟节点),实现一次重排插入多个节点
let myList = document.querySelector('.my-list');
let fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
let li = document.createElement('li');
li.innerText = i;
fragment.appendChild(li);
}
myList.appendChild(fragment);
-
处理动画时,使用will-change和transform 做优化
-- 使用will-change实现动画时,渲染引擎会将该元素单独实现一个图层,等这些变换发生时,渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。 -
避免多次读取offset、width、height等属性时,使用变量做缓存。
-- 当获取一些属性时,浏览器为了获得正确的值也会触发回流
2. 重绘
指修改了元素的外观样式,不会引起几何位置变化,直接入绘制阶段,生成绘制列表,然后执行之后的一系列子阶段,这个过程就叫重绘。如背景颜色、边框颜色,文字颜色等。
减少重排的方式:
- 减少逐项更改样式,使用 class合并一次性更新
//优化前,可能触发三次重绘
var el = document.querySelector('.el');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
//优化后,一次重绘完成
.active {
padding: 5px;
border-left: 1px;
border-right: 2px;
}
// js
var el = document.querySelector('.el');
el.className = 'active';
网友评论