web性能优化(Lighthouse和performance):从实际项目入手,如何监测性能问题、如何解决。

这篇文章总结一下我过往项目的web性能优化,主要是就项目中如何发现性能问题,优化如何解决,谈一下性能优化。把学习的过程跟你们分享一下,共同窗习。web的性能监测工具我用的是Chrome的Performance 面板和Lighthouse。javascript

回想起之前工做被产品拉进小黑屋,要我优化目前项目组的3个项目的性能问题。说体验起来比较卡。我带着激动地心,颤抖的手打开项目跑一下分数,广泛15-60分。由于咱们项目是数据可视化的项目。追求的是什么?酷炫吊炸天!!!!各类3D的地图,酷炫的动画,复杂的交互。优化……好吧,打工人打工魂。哦豁,优化好像依然没有达到预期的效果,通过不懈的努力仍是能有质的提高。由于公司项目是内网的(公司安全防范超过严格,啥也看不了),我是半行代码也拷不出来,也不敢贴出来呀,我尽可能把问题制造在dome里,也方便观察和解决。css

拓展插件,没有安装的安装看一下。若是有错误的地方但愿你们提出来,以便我能及时修改~html

1、 Lighthouse

Lighthouse生成的是一个报告,会给你的页面跑出一个分数来。 分别是页面性能(performance)、Progressive(渐进式 Web 应用)、Accessibility(可访问性)、Best Practices(最佳实践)、SEO 五项指标的跑分。甚至针对咱们的性能问题给出了可行的建议、以及每一项优化操做预期会帮咱们节省的时间。这份报告的可操做性是很强的——咱们只须要对着 LightHouse 给出的建议,一条一条地去尝试,就能够看到本身的页面,在一秒一秒地变快。vue

打开项目跑一下分数,看看都有什么问题(分数每次测会有误差,并且跟网速也有关系,因此不用太在乎分数,关心一下Lighthouse给咱们的建议)还有我的以为在生产环境测会比本地靠谱java

WechatIMG89.jpeg

那让咱们为了写个dome look look ,把问题制造出来。为了快速,我写的dome是用vuecli 4.0因此不少webpack配置是内置的,但我实际工做用的是react,不管脚手架怎么变,原理不会变。

1. Use HTPP/2

嗯,这个这个,HTTP/2的优势我就很少说了,你们能够去了解一下。15年发表到如今已经很成熟了,可使用起来~react

2. Remove unused CSS 或者 Remove unused JavaScript(移除无用的js和css)

考虑一下按需引入和CDN(这个在实际项目中会比较复杂,由于代码比较多。分析问题比较难)webpack

我随便加了几个第三方库,写了几个页面。首先看看大小,明明打包出来压缩的是493k的怎么加载时1643k呢nginx

WechatIMG95.jpeg

WechatIMG94.jpeg

去看看nginx配置,哦豁,示范的时候,我把开启Gzip注释了,放开放开,以后看看正常了。web

WechatIMG93.png

WechatIMG92.jpeg
目测打包出来1M多的chunk.js也是有点大,webpack广泛优化:切割一下打包文件,不要所有打包到一块儿。全局引入了echarts和element咱们把这两个js分开打包, 使用optimization.splitChunksnpm

WechatIMG99.jpeg WechatIMG98.jpeg

结果

WechatIMG99.jpeg

WechatIMG100.jpeg

我把压缩释放看看,点开Coverage面板,刷新看看。能够看到那些是关键资源(红色非关键,蓝色关键资源)。

WechatIMG75.png

点击查看详细加载状况,红色的就是没有使用的代码,可是打包压缩后的代码,咱们通常都看不出来是人仍是鬼。因此咱们尽可能对代码进行切割,不只能够减小大文件加载的时间,也能够明确问题所在。

WX20210518-000301@2x.png

咱们能够处理一下第三方的js,能看到echarts和elemenet-ui加载的大小和实际使用的大小有出入,通常Lighthouse用超过20 kb的未使用代码标记每一个JavaScript文件。咱们改变一下element-ui的引入方式,目前是全局引入,咱们可使用按需引入,由于目前我只用到了Button,咱们就只须要引入Button。优化后打包大小明显变小。

WechatIMG103.png

WechatIMG101.png

image.png 若是项目使用的组件比较多,按需引入不方便,必须全局引入。这时候考虑一下,由于第三方的库咱们不会常常去更新的,可使用cdn的方式引入,就减免打包的大小。

image.png

image.png

配置(我vue也不怎么用,你们参照参考官网配置就好,注意的是vue必需要先加载了,否则element-ui会报错。若是开发环境使用**min.js可能没法调试) WechatIMG83.png 使用(再也不须要import,其余正常使用就好)

WechatIMG82.png

WechatIMG104.jpeg

3.Serve static assets with an efficient cache policy(为静态资源提供缓存)

对于不常改变的静态资源好比说css、image等能够进行缓存,针对缓存也总结了一下,能够看看

打开ngnix配置文件,把缓存配上(这里粗暴把js和css缓存了,实际项目根据实际须要配置缓存) uuuu.png

image

image

4.Minimize main-thread work 最小化主线程工做

浏览器的渲染器过程将您的代码转换为用户能够与之交互的网页。默认状况下,渲染器进程的主线程一般处理大多数代码:它解析HTML并构建DOM,解析CSS并应用指定的样式,并解析,评估并执行JavaScript。主线程还处理用户事件。所以,每当主线程忙于执行其余操做时,您的网页就可能没法响应用户交互,从而致使不良的体验。

看看dome的例子,主要渲染时间在Style&&Layout,说明咱们的重排重绘时间占了主要时间,那咱们就想办法减小重排,这部分在performance面板看比较直观,下面performance会讲到这个例子的处理,这里跳过。 image.png

Google对咱们的建议是:

这个会比较笼统,只能根据项目一点一点改变, 样式、布局和渲染下面performance会介绍到

  1. 脚本评估
    • 优化第三方JavaScript
    • 消除您的输入处理程序
    • 使用网络工做者
  2. 样式和布局
    • 减小样式计算的范围和复杂性
    • 避免大型,复杂的布局和布局颠簸
  3. 渲染
    • 坚持只使用合成器属性并管理层数
    • 简化paint复杂性并减小paint面积
  4. 解析HTML和CSS
    • 提取关键CSS
    • 缩小CSS
    • 推迟非关键CSS
  5. 脚本解析和编译
    • 经过代码拆分减小JavaScript负载
    • 删除未使用的代码
如何分析非关键代码

上面有提过 Coverage Tool

图片能够看出这个页面其实根本就不使用到element-ui css, 咱们能够设置延迟加载非关键CSS image.png

webpack设置 html-critical-webpack-plugin

image.png 效果 image.png

原理

  • link rel="preload" as="style"异步请求样式表。您能够preload在《预载关键资产》指南中了解更多信息。
  • onload属性link容许CSS在加载完成后进行处理,在这里执行null转化,能够避免在切换rel属性时重复处理
  • noscript元素对不支持javascript的浏览器作兼容。
5.Ensure text remains visible during webfont load确保文本在Webfont加载期间保持可见

WechatIMG78.png

  • FOIT是浏览器在加载字体的时候的默认表现形式,也就是在字体加载过程当中,页面是看不到文本内容的。在现代浏览器中,FOIT会致使这种现象出现至多3秒。FOIT会致使不好的用户体验,这是咱们须要尽可能去避免的.
  • FOUT意思是在字体加载过程当中使用默认的系统字体,字体加载完后显示加载的字体,若是超过了FOIT(3s)字体还没加载,则继续使用默认的系统字体。
  • swap告诉浏览器使用字体的文本应当即使用系统字体显示。自定义字体准备好后,它将替换系统字体。能够避免在大多数现代浏览器中使用FOIT(并不是全部主流浏览器都支持font-display: swap)

WechatIMG79.png

7. Reduce JavaScript execution time 减小js的执行时间

当JavaScript执行时间超过2秒时,Lighthouse将显示警告。执行时间超过3.5秒时,审核将失败

建议(这些webpack都有相关的配置):

  • 拆分代码。
  • 缩小并压缩代码
  • 删除未使用的代码 (tree shaking)
  • 使用缓存代码(上面有讲)
8. Avoid enormous network payloads 避免大量的网络负载

这个涉及因素比较多,考虑从多方面入手,参考下面方法:

减小网络负载方法

  • 将请求推迟到须要时再发送。有关的方法,请参阅PRPL模式。
  • 最小化和压缩网络负载。
  • 对图像使用WebP而不是JPEG或PNG(图片要求不严格,能够压缩体积,我经常使用的在线压缩网站tinypng.com/)。
  • 将JPEG图像的压缩级别设置为85。
  • 缓存请求,以使页面在重复访问时不会从新下载资源。(请参阅“网络可靠性”登陆页面,以了解缓存的工做原理以及实现方法。)

PRPL

  • Push(推送或预加载)最重要的资源。
  • Render:尽快渲染初始路线。
  • Pre-cache 预缓存剩余资源。
  • Lazy load 延迟加载其余路由和非关键资源。

好比:vuecli3.x or 4.x默认打包以后,部署到服务器上的项目,会对静态资源的标签上默认加载preload或者prefetch属性(preload主要用于预加载当前页面须要的资源;而prefetch主要用于加载未来页面可能须要的资源)

预加载&&延迟加载

  • preload :是一种声明式的资源获取请求方式,用于提早加载一些须要的依赖,而且不会影响页面的onload事件。经过在HTML文档的开头添加标记rel="preload"来预加载关键资源,浏览器为资源设置了更合适的优先级,若是as属性被省略,那么该请求将会当作异步请求处理:

<link rel="preload" as="style" href="css/style.css">

  • prefetch :是一种利用浏览器的空闲时间加载页面未来可能用到的资源的一种机制;一般能够用于加载非首页的其余页面所须要的资源,以便加快后续页面的首屏速度;

  • 延迟加载 :是一种根据须要而不是预先加载资源的策略。这种方法在初始页面加载期间释放了资源,并避免了加载从未使用过的资产。

    若是您在网页上加载许多图像,请在加载页面时推迟全部折叠如下或设备视口以外的图像(请参阅使用lazysizes延迟加载图像)。

9. Eliminate render-blocking resources消除渲染阻止资源

image.png

在浏览器能够呈现任何内容以前,它须要将HTML标记解析为DOM树。若是遇到任何外部样式表(<link rel="stylesheet" />)或同步JavaScript标记(<script src="main.js"></script>),HTML解析器将暂停。 脚本和样式表都是渲染阻塞资源,这些资源会延迟FCP,从而延迟LCP。推迟使用任何非关键的JavaScript和CSS来加快网页主要内容的加载。 减小CSS阻断时间:

  • 缩小CSS: 对于Webpack:optimize-css-assets-webpack-plugin
  • 推迟非关键CSS
  • 内联关键CSS
11.Defer offscreen images 延迟加载具备lazysizes的屏幕外图像

延迟加载图片应该常常会用到,就不示范了 vue-lazyload / lazysizes.min.js

WechatIMG80.png

13. Image elements do not have explicit width and height 图片设置宽高

这个很好理解就不写案例了————

始终在图像和视频元素上包括width和设置height尺寸属性。以确保在浏览器开始获取图像以前在页面上分配了足够的空间。这将最大程度地减小回流和从新布局。

<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons" />

或者设置height:auto 自适应保真比例也能够

CLS较差的最多见缘由是:
  • 图片无尺寸
  • 没有尺寸的广告,嵌入和iframe
  • 动态注入的内容
  • Web字体致使FOIT / FOUT
  • 在更新DOM以前等待网络响应的操做

2、 Performance(当页面卡顿、慢时可使用Performance来分析问题)

lighthouse生成一个报告有些参数来源于performance,相对比lighthouse的分数和建议,performance用于记录和分析咱们的应用在运行时的全部活动。它呈现的数据具备实时性、多维度的特色,能够帮助咱们很好地定位性能问题。

好比下图,咱们看到style/Layout耗时很是夸张,可是我定位不到准确的地方,这个时候咱们用performance面板看看 image.png Performance总体分析

WechatIMG57.png

想了解Performance呈现的结果,须要知道浏览器的渲染知识,这里大概罗列一下(懂的能够跳过,直接看后面案例分析)。
1. Performance指标值
名词 解析 详细
FP (First Paint) 首次绘制 标记浏览器渲染任何在视觉上不一样于导航前屏幕内容以内容的时间点
FCP (First Contentful Paint) 首次内容绘制 标记浏览器渲染来自 DOM 第一位内容的时间点,该内容多是文本、图像、非空白canvas或SVG 甚至 元素.
LCP (Largest Contentful Paint) 最大内容渲染 表明在viewport中最大的页面元素加载的时间. LCP的数据会经过PerformanceEntry对象记录, 每次出现更大的内容渲染, 则会产生一个新的PerformanceEntry对象.
DCL (Dom Content loaded) 当 HTML文档被彻底加载和解析完成以后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载
FMP(First Meaningful Paint) 首次有效绘制
L (onLoad) 加载完成 当依赖的资源, 所有加载完毕以后才会触发.
TTI (Time to Interactive) 可交互时间 指标用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点.
TBT (Total Blocking Time) 页面阻塞总时长 TBT汇总全部加载过程当中阻塞用户操做的时长,在FCP和TTI之间任何long task中阻塞部分都会被汇总
FID (First Input Delay) 首次输入延迟 指标衡量的是从用户首次与您的网站进行交互(即当他们单击连接,点击按钮等)到浏览器实际可以访问之间的时间
CLS (Cumulative Layout Shift) 累积布局偏 总结起来就是一个元素初始时和其hidden之间的任什么时候间若是元素偏移了, 则会被计算进去。具体的计算方法可看这篇文章 《Cumulative Layout Shift (CLS)》
SI (Speed Index) 指标用于显示页面可见部分的显示速度, 单位是时间

2. RAIL 性能模型

名词 解析 详细
response 响应 用户输入以后是否能在100ms以内响应
这里的输入包括点击按钮、切换表单控件等,但不包括触摸滑动或滚动(50ms内完成较好)
animation 动画 最近手机圈很流行将屏幕刷新率提高为90hz,这里hz就是帧率,90hz就是每秒有90帧,一帧就是一个画面。每秒看到的画面越多,咱们就会感到越流畅,(每10ms内产生一帧较好)
idle 浏览器空置状态 利用空闲的时间完成一些推迟的工做。推迟的工做应分为50ms的多个块进行。(尽量增长空闲时间)
load 加载 5s加载完成而且能够交互

3.Performancem面板参数

【第3如下配置都是用来模拟手机、慢网络下使用的】

名词 解析
no recordings 就是每一次的检测报告,能够根据每一次的检测报告,去进行性能优化的对比
Screenshots 是用来查看在每一个时间段界面的变化
Memory 存储调用栈的大小,在不一样时间段的不一样大小;
Disable Javascript samples 禁用 javascript 调用栈,关闭javaScript样本减小在手机运行时的开销,模拟手机运行时勾选
Enable advanced paint instrumentation (slow) 记录渲染事件的细节,选择frames中的一块,能够看到区域四多了个Layers
Network 网络模拟,能够模拟在3G,4G等网络条件下运行页面;
CPU 用来查看电脑的性能问题,主要为了模拟底CPU下运行性能
HEAP JavaScrip 执行的时间分布。
区域2:网页性能总览图(overview)
名词 解析
FPS 每秒帧数,是用来分析动画的一个主要性能指标,对于动画而言标准是保持在60FPS。绿色越高越好,出现红色则表示FPS低(这就是你为啥以为页面卡顿了),你能够在区域三Frames中看到具体的FPS值(见下面第二图)
CPU 处理各个任务花费的时间,选择一段CPU统计能够在区域四的Summary看到统计表格
Scripting 脚本
Rendering 渲染
Painting 绘制
Loading 加载
ldle 闲置
NET 每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每一个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)。
区域3:线程面板
名词 解析
Frames 帧线程,鼠标悬浮绿色块能够看到fps
Main 主线程,负责执行Javascript, 解析HTML/CSS, 完成绘制。
能够看到主线程调用栈和耗时状况,每一个长条都是一个事件,悬浮能够看到耗时和事件名
x轴指时间: 最上面的第一条就是事件触发的地方,直到结束,这条线是最长的
y轴指调用栈:上面的event调用了下面的子event,越到下面数量越少(瀑布)
Raster Raster线程,负责完成某个layer或者某些块(tile)的绘制。光栅化线程池,用来让 GPU执行光栅化的任务
Interactions 用来记录用户交互操做,好比点击鼠标、输入文字、动画等
Timings 用来记录一些关键的时间节点在什么时候产生的数据信息,诸如 FP、FCP、LCP 等
Compositor 合成线程的执行记录,用来记录html绘制阶段 (Paint)结束后的图层合成操做

区域4:统计面板

名词 解析
Summary 统计图:展现各个事件阶段耗费的时间
Bottom-Up 排序:能够看到各个事件消耗时间排序
(1)self-time 指除去子事件这个事件自己消耗的时间
(2)total-time 这个事件从开始到结束消耗的时间(包含子事件)
Call Tree 调用栈:Main选择一个事件,表示事件调用顺序列表(从最顶层到最底层,而不是只有当前事件)
Event Log 事件日志
(1) 多了个start time,指事件在多少毫秒开始触发的
(2) 右边有事件描述信息
下面举两个例子来了解一下,怎么找出性能问题,而且解决

一、下面这张图,动画看起来是有点卡顿的问题的,利用performance测试一下,能够看到Main面板里面展开看到不少Task右上角都有红色的三角形,说明是个Long Task,注意Animation Frame Fired事件右上角的红色三角形图标,该红色三角图标是这个事件有问题的警告。 bbb.gif

选中其中一个Animation Frame Fired 查看详细信息,能够看到有问题的代码在第几行,点击代码会跳到指定代码 WechatIMG60.png

再放大能够看到Animation Frame Fired有不少紫色的Layout模块,右上角也有红色三角形。点击选中Forced reflow查看信息,看到形成卡顿的缘由是不少元素进行不断的回流和重绘。

WechatIMG61.png

看一下代码,有1000个元素在不断经过他的offsetTop改变宽(若是你电脑性能比较好,能够改为5000个,卡顿效果更佳明显)。当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,须要经过即时计算获得,因此也进行回流(重排)。咱们没法避免读取属性,可是不用每个元素读取完offsetTop再立刻赋值,咱们能够先批量读取完属性后再赋值.能够利用开源的fastdom来解决这个问题(能够去看看它的源码,没有多少行)

image.png

2.使用fastdom优化的代码(读写分离)

WechatIMG63.png

3.优化先后效果对比

优化前 bbb.gif 优化后 aaa.gif

再举个简单,无伤大雅的例子

下面是须要拖拽线条位置改变方格大小(固然了,只写了线条拖拽功能),再performance上看到有不少layout Shift(布局偏移)

ccc.gif

放大Mian WechatIMG65.png

看代码,利用定位改变left的位置进行移动的,这样会形成重排。咱们能够利用transform代替,减小重排。 WechatIMG66.png

优化后 image.png 效果 WechatIMG67.png

参考文献:

Google 的 web.dev/

相关文章
相关标签/搜索