美文网首页
前端性能优化

前端性能优化

作者: 攻城狮_前端程序媛 | 来源:发表于2022-07-25 19:28 被阅读0次

参考文档:
浏览器工作原理与实践—李兵
http://www.zyiz.net/tech/detail-134416.html

当打开一个页面,用户可能会看到这样一个变化过程:白屏 → 打底图 → 出现部分内容 → 首屏内容全部出现,但图片还在加载中 → 首屏内容全部出现,图片也已经加载完成。

一、Web性能指标

Web性能指标.png

1 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
  1. 首先 Send request,该过程表示网络请求已被发送。然后该任务进入了等待状态;

  2. 接着由网络进程负责下载资源,当接收到响应头的时候,该任务便执行 Receive Respone 过程,该过程表示接收到 HTTP 的响应头了;

  3. 接着执行 DOM 事件:pagehide、visibilitychange 和 unload 等事件,如果你注册了这些事件的回调函数,那么这些回调函数会依次在该任务中被调用;

  4. DOM 事件执行完成之后,就开始接收 HTML 数据了,这体现在了 Recive Data 过程;
    -- Recive Data 过程表示请求的数据已被接收,如果 HTML 数据过多,会存在多个 Receive Data 过程

  5. 等到所有的数据都接收完成之后,渲染进程会触发Finish load ,该过程表示网络请求已经完成;

  6. 数据都接收完成之后,便进入 HTML 解析阶段(HTMLParser);
    -- 在 ParserHTML 的过程中,如果解析到了 script 标签,那么便进入了脚本执行过程,也就是图中的 Evalute Script;
    -- 执行一段脚本时,首先会进行编译,于是在 Evalute Script 过程中,先进入了脚本编译过程,也就是图中的 Complie Script;
    -- 脚本编译好之后,就进入程序执行过程,执行全局代码时,V8 会先构造一个 anonymous 过程,在执行 anonymous 过程中,会调用 setNewArea 过程,setNewArea 过程中又调用了 createElement,触发 DOM 内容的修改,所以又强制执行了 ParserHTML 过程生成的新的 DOM。

  7. DOM 生成完成之后,会触发相关的 DOM 事件,如 DOMContentLoaded,还有 readyStateChanged;

  8. 然后ParserHTML 过程继续计算样式表,即 Reculate Style,生成 CSSOM;

  9. 在生成完了 DOM 和 CSSOM 之后,渲染主线程首先会执行了一些 DOM 监听事件,如 readyStateChange、load、pageshow;

  10. 有了DOM 和 CSSOM 之后,开始进入布局阶段,这个过程对应图中的 Layout;
    -- 根据DOM 和 CSSOM 生成布局树(LayoutTree),
    -- 根据布局树进行布局计算,计算出布局树的每个节点在浏览器屏幕上的具体坐标位置。

  11. 根据布局计算结果更新图层树 (LayerTree),这个过程对应图中的 Update LayerTree ;

  12. 有了图层树之后,染引擎会对图层树中的每个图层进行绘制,生成待【绘制列表】,这个过程就称为 Paint;

13.有了绘制列表,把绘制列表交给合成线程,就会进入栅格化节点,生成位图,这个过程对应图中的 Composite Layers;

  1. 合成线程维护了一个 Raster 线程池,线程池中的每个线程称为 Rasterize,用来执行光栅化操作,对应的任务就是 Rasterize Paint。由于光栅化操作是在 GPU 进程中执行的,因此 Rasterize 线程需要和 GPU 线程保持通信。

  2. 然后 GPU 生成图像,GPU进程根据不同图块生成位图,还给合成线程,合成线程将图层提交给浏览器进程,

  3. 浏览器进程将其合成并生成页面,最后再将页面显示在屏幕上。

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元素
  • 元素位置改变
  • 元素尺寸改变
  • 元素内容改变
  • 页面渲染器初始化
  • 浏览器窗口大小发生改变
减少重排的方式:
  1. 使用文档片段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);
  1. 处理动画时,使用will-change和transform 做优化
    -- 使用will-change实现动画时,渲染引擎会将该元素单独实现一个图层,等这些变换发生时,渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。

  2. 避免多次读取offset、width、height等属性时,使用变量做缓存。
    -- 当获取一些属性时,浏览器为了获得正确的值也会触发回流

2. 重绘

指修改了元素的外观样式,不会引起几何位置变化,直接入绘制阶段,生成绘制列表,然后执行之后的一系列子阶段,这个过程就叫重绘。如背景颜色、边框颜色,文字颜色等。

减少重排的方式:
  1. 减少逐项更改样式,使用 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';

相关文章

  • 前端性能优化(中)

    性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 《前端性能优化(上)...

  • 前端性能优化(下)

    性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 《前端性能优化(中)...

  • 前端性能优化(上)

    性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 为什么要进行前端性能...

  • 前端面试必问及加分点---性能优化篇

    如何进行网站性能优化 你有用过哪些前端性能优化的方法? 谈谈性能优化问题 代码层面的优化 前端性能优化最佳实践

  • 前端性能优化

    对于前端的性能优化,主要有分为加载优化和性能优化。在Google官方文档中,给出了性能优化有哪些好处。前端优化的最...

  • 常用的后端性能优化六种方式:缓存化+服务化+异步化等

    性能优化专题 前端性能优化 数据库性能优化 jvm和多线程优化 架构层面优化 缓存性能优化 常用的后端性能优化六大...

  • 关于前端性能优化问题详解

    关于前端性能优化问题详解 出处:http://segmentfault.com/blogs 前端性能优化指南 AJ...

  • 前端性能优化

    前端性能优化 下面是我认知的前端性能优化的策略,本书主要着手 JavaScript 优化展开阐述。 JavaScr...

  • 2019-10-22

    2018 前端性能优化清单

  • 2020-04-11

    前端工程化相关 前端动画相关 优化前端性能

网友评论

      本文标题:前端性能优化

      本文链接:https://www.haomeiwen.com/subject/mpugdltx.html