美文网首页
前端性能优化

前端性能优化

作者: 攻城狮_前端程序媛 | 来源:发表于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';
    

    相关文章

      网友评论

          本文标题:前端性能优化

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