从实际项目入手,完成web性能监控及优化转载
2年前
523000
导语
这篇文章总结一下我过往项目的web性能优化,主要是就项目中如何发现性能问题,优化如何解决,谈一下性能优化。把学习的过程跟大家分享一下,共同学习。web的性能监测工具我用的是Chrome的Performance 面板和Lighthouse。
正文
回想起以前工作被产品拉进小黑屋,要我优化目前项目组的3个项目的性能问题。说体验起来比较卡。我带着激动地心,颤抖的手打开项目跑一下分数,普遍15-60分。因为我们项目是数据可视化的项目。追求的是什么?酷炫吊炸天!!!!各种3D的地图,酷炫的动画,复杂的交互。优化……好吧,打工人打工魂。哦豁,优化好像依然没有达到预期的效果,经过不懈的努力还是能有质的提升。因为公司项目是内网的(公司安全防范超过严格,啥也看不了),我是半行代码也拷不出来,也不敢贴出来呀,我尽量把问题制造在dome里,也方便观察和解决。
拓展插件,没有安装的安装看一下。如果有错误的地方希望大家提出来,以便我能及时修改~
一、 Lighthouse
Lighthouse生成的是一个报告,会给你的页面跑出一个分数来。分别是页面性能(performance)、Progressive(渐进式 Web 应用)、Accessibility(可访问性)、Best Practices(最佳实践)、SEO 五项指标的跑分。甚至针对我们的性能问题给出了可行的建议、以及每一项优化操作预期会帮我们节省的时间。这份报告的可操作性是很强的——我们只需要对着 LightHouse 给出的建议,一条一条地去尝试,就可以看到自己的页面,在一秒一秒地变快。
打开项目跑一下分数,看看都有什么问题(分数每次测会有偏差,而且跟网速也有关系,所以不用太在意分数,关心一下Lighthouse给我们的建议)还有个人觉得在生产环境测会比本地靠谱
那让我们为了写个dome look look ,把问题制造出来。为了快速,我写的dome是用vuecli 4.0所以很多webpack配置是内置的,但我实际工作用的是react,无论脚手架怎么变,原理不会变。
1. Use HTPP/2
嗯,这个这个,HTTP/2的优点我就不多说了,大家可以去了解一下。15年发表到现在已经很成熟了,可以使用起来~
2. Remove unused CSS 或者 Remove unused JavaScript(移除无用的js和css)
考虑一下按需引入和CDN(这个在实际项目中会比较复杂,因为代码比较多。分析问题比较难)
我随便加了几个第三方库,写了几个页面。首先看看大小,明明打包出来压缩的是493k的怎么加载时1643k呢
去看看nginx配置,哦豁,示范的时候,我把开启Gzip注释了,放开放开,之后看看正常了。
目测打包出来1M多的chunk.js也是有点大,webpack普遍优化:切割一下打包文件,不要全部打包到一起。全局引入了echarts和element我们把这两个js分开打包, 使用optimization.splitChunks
结果
我把压缩释放看看,点开Coverage面板,刷新看看。可以看到那些是关键资源(红色非关键,蓝色关键资源)。
点击查看详细加载情况,红色的就是没有使用的代码,但是打包压缩后的代码,我们一般都看不出来是人还是鬼。所以我们尽量对代码进行切割,不仅可以减少大文件加载的时间,也可以明确问题所在。
我们可以处理一下第三方的js,能看到echarts和elemenet-ui加载的大小和实际使用的大小有出入,一般Lighthouse用超过20 kb的未使用代码标记每个JavaScript文件。我们改变一下element-ui的引入方式,目前是全局引入,我们可以使用按需引入,因为目前我只用到了Button,我们就只需要引入Button。优化后打包大小明显变小。
配置(我vue也不怎么用,大家参照参考官网配置就好,注意的是vue必须要先加载了,不然element-ui会报错。如果开发环境使用**min.js可能无法调试) 图片 使用(不再需要import,其他正常使用就好)
3.Serve static assets with an efficient cache policy(为静态资源提供缓存)
对于不常改变的静态资源比如说css、image等可以进行缓存,针对缓存也总结了一下,可以看看
打开ngnix配置文件,把缓存配上(这里粗暴把js和css缓存了,实际项目根据实际需要配置缓存)
4.Minimize main-thread work最小化主线程工作
浏览器的渲染器过程将您的代码转换为用户可以与之交互的网页。默认情况下,渲染器进程的主线程通常处理大多数代码:它解析HTML并构建DOM,解析CSS并应用指定的样式,并解析,评估并执行JavaScript。主线程还处理用户事件。因此,每当主线程忙于执行其他操作时,您的网页就可能无法响应用户交互,从而导致不良的体验。
看看dome的例子,主要渲染时间在Style&&Layout,说明我们的重排重绘时间占了主要时间,那我们就想办法减少重排,这部分在performance面板看比较直观,下面performance会讲到这个例子的处理,这里跳过。
Google对我们的建议是:
这个会比较笼统,只能根据项目一点一点改变, 样式、布局和渲染下面performance会介绍到
1、脚本评估
- 优化第三方JavaScript
- 消除您的输入处理程序
- 使用网络工作者
2、样式和布局
- 减少样式计算的范围和复杂性
- 避免大型,复杂的布局和布局颠簸
3、渲染
- 坚持只使用合成器属性并管理层数
- 简化paint复杂性并减少paint面积
4、解析HTML和CSS
- 提取关键CSS
- 缩小CSS
- 推迟非关键CSS
5、脚本解析和编译
- 通过代码拆分减少JavaScript负载
- 删除未使用的代码
如何分析非关键代码
上面有提过 Coverage Tool
可以看出这个页面其实根本就不使用到element-ui css, 我们可以设置延迟加载非关键CSS图片
webpack设置 html-critical-webpack-plugin
效果
原理
- link rel="preload" as="style"异步请求样式表。您可以preload在《预载关键资产》指南[2]中了解更多信息。
- o/n/l/o/a/d属性link允许CSS在加载完成后进行处理,在这里执行null转化,可以避免在切换rel属性时重复处理
- noscript元素对不支持javascript的浏览器做兼容。
5.Ensure text remains visible during webfont load确保文本在Webfont加载期间保持可见
- FOIT是浏览器在加载字体的时候的默认表现形式,也就是在字体加载过程中,页面是看不到文本内容的。在现代浏览器中,FOIT会导致这种现象出现至多3秒。FOIT会导致很差的用户体验,这是我们需要尽量去避免的.
- FOUT意思是在字体加载过程中使用默认的系统字体,字体加载完后显示加载的字体,如果超过了FOIT(3s)字体还没加载,则继续使用默认的系统字体。
- swap告诉浏览器使用字体的文本应立即使用系统字体显示。自定义字体准备好后,它将替换系统字体。可以避免在大多数现代浏览器中使用FOIT(并非所有主流浏览器都支持font-display: swap)
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 :是一种声明式的资源获取请求方式,用于提前加载一些需要的依赖,并且不会影响页面的o/n/l/oad事件。通过在HTML文档的开头添加标记rel="preload"来预加载关键资源,浏览器为资源设置了更合适的优先级,如果as属性被省略,那么该请求将会当做异步请求处理:
<link rel="preload" as="style" href="css/style.css">
prefetch :是一种利用浏览器的空闲时间加载页面将来可能用到的资源的一种机制;通常可以用于加载非首页的其他页面所需要的资源,以便加快后续页面的首屏速度;
延迟加载 :是一种根据需要而不是预先加载资源的策略。这种方法在初始页面加载期间释放了资源,并避免了加载从未使用过的资产。
如果您在网页上加载许多图像,请在加载页面时推迟所有折叠以下或设备视口之外的图像(请参阅使用lazysizes[7]延迟加载图像)。
9. Eliminate render-blocking resources消除渲染阻止资源
在浏览器可以呈现任何内容之前,它需要将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
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之前等待网络响应的操作
二、 Performance(当页面卡顿、慢时可以使用Performance来分析问题)
lighthouse生成一个报告有些参数来源于performance,相对比lighthouse的分数和建议,performance用于记录和分析我们的应用在运行时的所有活动。它呈现的数据具有实时性、**度的特点,可以帮助我们很好地定位性能问题。
比如下图,我们看到style/Layout耗时非常夸张,但是我定位不到准确的地方,这个时候我们用performance面板看看
下面举两个例子来了解一下,怎么找出性能问题,并且解决
1、下面这张图,动画看起来是有点卡顿的问题的,利用performance测试一下,可以看到Main面板里面展开看到很多Task右上角都有红色的三角形,说明是个Long Task,注意Animation Frame Fired事件右上角的红色三角形图标,该红色三角图标是这个事件有问题的警告。
再放大可以看到Animation Frame Fired有很多紫色的Layout模块,右上角也有红色三角形。点击选中Forced reflow查看信息,看到造成卡顿的原因是很多元素进行不断的回流和重绘。
看一下代码,有1000个元素在不断通过他的offsetTop改变宽(如果你电脑性能比较好,可以改成5000个,卡顿效果更佳明显)。当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,需要通过即时计算得到,所以也进行回流(重排)。我们无法避免读取属性,但是不用每一个元素读取完offsetTop再马上赋值,我们可以先批量读取完属性后再赋值.可以利用开源的fastdom[12]来解决这个问题(可以去看看它的源码,没有多少行)
2.使用fastdom优化的代码(读写分离)
再举个简单,无伤大雅的例子
下面是需要拖拽线条位置改变方格大小(当然了,只写了线条拖拽功能),再performance上看到有很多layout Shift(布局偏移)
放大Mian
看代码,利用定位改变left的位置进行移动的,这样会造成重排。我们可以利用transform代替,减少重排。
优化后
更多思考
本篇介绍的是web性能的监控和优化,更多关于web性能测试和优化的内容大家可以阅读以下内容加深阅读
webpack构建速度和体积优化策略
核心web指标对业务的影响
点赞收藏
分类: